MediaWiki:Gadget-addMe-WishlistSurvey.js: Difference between revisions

From Meta, a Wikimedia project coordination wiki
Content deleted Content added
fix interface translation
fix: there is no /en page for English, so it why it was broken for all languages in the first place
Line 349: Line 349:
api.get( { action: 'query', titles: interfaceMessagesFullPath + '/' + util.userLanguage() + '|' + configFullPath + '/' + util.userLanguage() , format: 'json' } ).then( function ( data ) {
api.get( { action: 'query', titles: interfaceMessagesFullPath + '/' + util.userLanguage() + '|' + configFullPath + '/' + util.userLanguage() , format: 'json' } ).then( function ( data ) {
for ( var id in data.query.pages ) {
for ( var id in data.query.pages ) {
if ( data.query.pages[ id ].title === util.interfaceMessagesPath + '/' + util.userLanguage() ) {
console.log( id );
if ( data.query.pages[ id ].title === util.interfaceMessagesPath + '/' + util.userLanguage() && id > 0 ) {
interfaceMessagesFullPath = util.interfaceMessagesPath + '/' + ( id === -1 ? 'en' : util.userLanguage() );
interfaceMessagesFullPath = util.interfaceMessagesPath + '/' + util.userLanguage();
}
}
if ( data.query.pages[ id ].title === util.configPath + '/' + util.contentLanguage() ) {
if ( data.query.pages[ id ].title === util.configPath + '/' + util.contentLanguage() && id > 0 ) {
configFullPath = util.configPath + '/' + ( id === -1 ? 'en' : util.userLanguage() );
configFullPath = util.configPath + '/' + util.userLanguage();
}
}
}
}

Revision as of 16:50, 29 January 2022

// <nowiki>
/**
 * Fork of [[MediaWiki:Gadget-addMe.js]] for use in the [[Community Wishlist Survey]].
 * This gadget will be default-on until voting ends on December 10.
 * Please direct inquiries/concerns to [[Talk:Community Wishlist Survey]] or [[User talk:MusikAnimal (WMF)]].
 */
/* global RJSON */
/* eslint-disable no-implicit-globals, one-var, vars-on-top, no-jquery/no-global-selector */
/* eslint-disable no-jquery/no-trim, new-cap, no-useless-concat */
/* eslint-disable no-jquery/no-parse-html-literal, no-multi-str */
/* eslint-disable no-console */
/*
 * Common utilities for both the endorse & the join gadget
 */
var gadgetUtilities = function () {
	// A reference to the object
	var that = this;

	/*
	 * The interface messages or strings are maintained in interfaceMessagesPath & config values eg,
	 * section-header, the section where the comments are added etc are maintained in configPath
	 */
	this.interfaceMessagesPath = 'Community Wishlist Survey/AddMe/InterfaceText';
	this.configPath = 'Community Wishlist Survey/AddMe/Config';

	// The time taken for the page to scroll to the feedback speech bubble (milliseconds)
	this.feedbackScrollTime = 2000;

	// The time taken for the feedback speech bubble to disappear (milliseconds)
	this.feedbackDisappearDelay = 10000;
	/*
	 * This function is used to set a cookie to show the speech bubble
	 * on page reload
	 */
	this.setFeedbackCookie = function () {
		mw.storage.set( 'showWishlistSurveyThanks', true );
	};
	/*
	 * This function is used to check if a has been set by the above function
	 * to show the speech bubble on page reload
	 */
	this.checkFeedbackCookie = function () {
		if ( mw.storage.get( 'showWishlistSurveyThanks' ) ) {
			mw.storage.remove( 'showWishlistSurveyThanks' );
			return true;
		} else {
			return false;
		}
	};
	/*
	 * To display an error message when an error occurs
	 * in the gadget
	 */
	this.showErrorMessage = function ( gadget, type ) {
		var errorAttr = '[localize=error-' + type + ']';
		var gadgetID = '.' + gadget;
		$( gadgetID + ' ' + errorAttr ).show();
	};
	/*
	 * To remove the error message displayed by the above function
	 */
	this.removeErrorMessage = function ( gadget ) {
		var gadgetID = '.' + gadget;
		$( gadgetID + ' [localize^="error-"]' ).hide();
	};
	/*
	 * To detect the type of grant. IEG,PEG etc
	 */
	this.grantType = function ( config ) {
		var grant = mw.config.get( 'wgTitle' ).split( '/' )[ 0 ].replace( / /g, '_' );
		if ( grant in config ) {
			return config[ grant ];
		} else {
			return config.default;
		}
	};
	/*
	 * To detect the users default language
	 */
	this.userLanguage = function () {
		return mw.config.get( 'wgUserLanguage' );
	};
	/*
	 * To detect the language of the page
	 */
	this.contentLanguage = function () {
		return mw.config.get( 'wgContentLanguage' );
	};
	/*
	 * To remove extra spaces & cleanup the comment string
	 */
	this.cleanupText = function ( text ) {
		text = $.trim( text ) + ' ';
		var indexOf = text.indexOf( '~~~~' );
		if ( indexOf === -1 ) {
			return text;
		} else {
			return text.slice( 0, indexOf ) + text.slice( indexOf + 4 );
		}
	};
	/*
	 * The config files which can be translated with the help of the
	 * translation tool generates the dict with the values having a
	 * lot of space in the key value pairs. This function strips the
	 * whitespace.
	 */
	this.stripWhiteSpace = function ( dict ) {
		for ( var key in dict ) {
			// Temp fix for section header
			if ( key === 'section-header' ) {
				dict[ 'section-header-read' ] = dict[ key ].replace( / /g, '_' );
				dict[ 'section-header-write' ] = dict[ key ];
			}
			dict[ key ] = typeof ( dict[ key ] ) === 'object' ? that.stripWhiteSpace( dict[ key ] ) : $.trim( dict[ key ] );
		}
		return dict;
	};
	/*
	 * The function creates the markup for the link to a
	 * user's user page
	 */
	this.addToInfobox = function ( username ) {
		return username;
	};
	/*
	 * To localize the gadget's interface messages based on the user's language setting
	 */
	this.localizeGadget = function ( gadgetClass, localizeDict ) {
		$( gadgetClass + ' [localize]' ).each( function () {
			var localizeValue = localizeDict[ $( this ).attr( 'localize' ) ];
			if ( $( this ).attr( 'value' ) ) {
				$( this ).attr( 'value', localizeValue );
			} else if ( $( this ).attr( 'placeholder' ) ) {
				$( this ).attr( 'placeholder', localizeValue );
			} else if ( $( this ).attr( 'data-placeholder' ) ) {
				$( this ).attr( 'data-placeholder', localizeValue );
			} else {
				$( this ).html( localizeValue );
			}
		} );
	};
	/*
	 * This function show the feedback speech bubble after an
	 * endorsement has been made or after joining a project
	 */
	this.showFeedback = function ( config ) {
		var $li = $( '#' + config[ 'section-header-read' ] ).parent().next().find( 'li' ).eq( -1 );
		var $speechBubble = $li.append( $( '<div class="grantsSpeechBubbleContainer"></div>' ).html( '<div class="grantsSpeechBubble">\
		<span>Thank you for participating in the survey!</span></div><div class="grantsSpeechBubbleArrowDown"></div>' ) ).find( '.grantsSpeechBubbleContainer' );
		var width = $li.css( 'display', 'inline-block' ).width();
		$li.css( 'display', '' );
		$li.css( 'position', 'relative' );
		$speechBubble.css( 'left', width / 2 + 'px' );
		$( 'body, html' ).animate( { scrollTop: $li[ 0 ].offsetTop }, that.feedbackScrollTime );
		setTimeout( function () {
			$speechBubble.hide();
		}, that.feedbackDisappearDelay );
	};
};
/*
 * The Endorse Gadget
 */
var endorseGadget = function () {
	/* Variables */
	var util = new gadgetUtilities();
	var dialog = null;

	var api = new mw.Api();
	var that = this;

	var supportTemplates = '{{\\s*(support|s|yes|vote+|Sì|賛成|موافق|strong support|weak support)\\s*}}';

	/*
	 * This function creates the dialog box for the gadget.
	 * It is also where all the dialog related interactions are defined.
	 */
	var createDialog = function () {
		dialog = $( "<div id='devEndorseDialog'></div>" ).html(
			'<div class="mw-ui-vform">\
				<div class="error grantsHide" localize="error-save">An error occurred</div>\
				<div class="error grantsHide" localize="error-login">An error occurred</div>\
			</div>\
			<div localize="message-description" class="messageDescription">Explaining your endorsement improves process</div>' + '\
			<textarea rows="5" cols="10" placeholder="Add your comment" id="devEndorseComment" class="" localize="placeholder-comment" style="color: #333;"></textarea>\
			<span localize="message-signature" class="messageSignature">Your signature will be added automatically</span>\
			<div class="gadgetControls">\
				<span localize="button-cancel" class="mw-ui-button cancel mw-ui-quiet">Cancel</span>\
				<input type="submit" localize="button-submit" class="mw-ui-button mw-ui-progressive addme-support" localize="button" value="Support"></input>\
			</div>'
		).dialog( {
			dialogClass: 'grantsGadget endorseGadget',
			autoOpen: false,
			title: '<span localize="title">Support proposal</span>',
			width: '495px',
			modal: true,
			closeOnEscape: true,
			resizable: false,
			draggable: false,
			close: function () {
				$( '#devEndorseComment' ).val( '' );
			}
		} );

		$( '.addme-support' ).on( 'click', function () {
			that.addEndorsement( util.cleanupText( $( '#devEndorseComment' ).val() ) );
		} );

		$( '.endorseGadget .cancel' ).on( 'click', function () {
			dialog.dialog( 'close' );
		} );

		util.localizeGadget( '.endorseGadget', that.interfaceMessages );
	};
	this.Dialog = function () {
		if ( dialog === null ) {
			createDialog();
		} else {
			dialog.dialog( 'open' );
		}
	};
	/*
	 * The main function to add the feedback/endorsement to the page. It first checks if the page has an endorsement section.
	 * If it dosent it creates a new section called Endorsements and appends the feedback/endorsement comment to that section,
	 * else it appends the feedback/endorsement comment to existing Endorsements section.
	 * The name of the endorsement section is defined in the config.
	 */
	this.addEndorsement = function ( text ) {
		// Remove support template for comment if present.
		text = text.replace( new RegExp( supportTemplates ), '' ).trim();

		var endorseComment = '\n* {{support}}' + ( text.length ? ' ' + text : '' ) + ' ~~~~' + '\n';
		var proposalUrl = 'https://meta.wikimedia.org' + ( new mw.Title( that.title ) ).getUrl();
		var summary = 'Support proposal' + ( text.length > 0 ? ': ' + text : '' ).trim();
		api.get( {
			format: 'json',
			action: 'parse',
			prop: 'sections',
			page: that.title,
			uselang: 'en'
		} ).then( function ( result ) {
			var sections = result.parse.sections;
			var sectionCount = 0;
			var sectionFound = false;
			for ( var section in sections ) {
				if ( sections[ section ].level > 3 ) {
					continue;
				}
				if ( $.trim( sections[ section ].anchor ) === that.config[ 'section-header-read' ] ) {
					sectionFound = true;
					break;
				}
				sectionCount++;
			}
			if ( sectionFound ) {
				api.get( {
					format: 'json',
					action: 'parse',
					prop: 'wikitext',
					page: that.title,
					section: sectionCount
				} ).then( function ( result ) {
					var wikitext = result.parse.wikitext[ '*' ];

					// Check if they've already voted.
					var regexp = new RegExp(
						supportTemplates + '.*?' +
						mw.config.get( 'wgUserName' ).replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' ) +
						'.*?\\(UTC\\)'
					);
					if ( regexp.test( wikitext ) && !window.confirm( 'It looks like you already voted on this proposal! Should we still attempt to add your vote?' ) ) {
						return $( '.endorseGadget .cancel' ).trigger( 'click' );
					}

					var endorsementSection = wikitext + endorseComment;
					api.post( {
						action: 'edit',
						title: that.title,
						text: endorsementSection,
						summary: summary,
						section: sectionCount,
						// 'watchlist':'watch',
						token: mw.user.tokens.get( 'csrfToken' )
					} ).then( function () {
						console.log( 'Successfully added endorsement' );
						window.location = proposalUrl;
						util.setFeedbackCookie();
					}, function () {
						util.showErrorMessage( 'endorseGadget', 'save' );
					} );
				} );
			} else {
				api.get( {
					format: 'json',
					action: 'parse',
					prop: 'wikitext',
					page: that.title,
					section: sectionCount
				} ).then( function ( result ) {
					var wikitext = result.parse.wikitext[ '*' ] + '\n=== ' + that.config[ 'section-header-write' ] + ' ===\n';

					api.post( {
						action: 'edit',
						title: that.title,
						text: wikitext + endorseComment,
						summary: summary,
						section: sectionCount,
						// 'watchlist': 'watch',
						token: mw.user.tokens.get( 'csrfToken' )
					} ).then( function () {
						console.log( 'Successfully added endorsement' );
						window.location = proposalUrl;
						util.setFeedbackCookie();
					}, function () {
						util.showErrorMessage( 'endorseGadget', 'save' );
					} );
				} );
			}
		}, function () {
			util.showErrorMessage( 'endorseGadget', 'save' );
		} );
	};
};

/* End of functions */
$( function () {
	if ( !$( '.wp-addme-button' ).length ) {
		// Ignore this page
		return;
	}
	mw.loader.using( [ 'jquery.ui', 'mediawiki.api', 'mediawiki.ui', 'jquery.chosen' ], function () {
		/*
		 * Fix mw.config.get('wgPageContentLanguage') == 'en') checking with a better solution,
		 * either when pages can be tagged with arbitary language or when we set langauge markers later on.
		 *
		 */
		if ( mw.config.get( 'wgPageContentLanguage' ) === 'en' ) {

			var endorse = new endorseGadget();
			var util = new gadgetUtilities();
			var api = new mw.Api();
			var interfaceMessagesFullPath = util.interfaceMessagesPath;
			var configFullPath = util.configPath;

			/*
			 * To detect if we have the gadget translations and config in the desired languages.
			 * Currently page language is English always. So the config returned is in en. The InterfaceMessages is
			 * in the user's language
			 */
			api.get( { action: 'query', titles: interfaceMessagesFullPath + '/' + util.userLanguage()  + '|' + configFullPath + '/' + util.userLanguage() , format: 'json' } ).then( function ( data ) {
				for ( var id in data.query.pages ) {
					console.log( id );
					if ( data.query.pages[ id ].title === util.interfaceMessagesPath + '/' + util.userLanguage() && id > 0 ) {
						interfaceMessagesFullPath = util.interfaceMessagesPath + '/' + util.userLanguage();
					}
					if ( data.query.pages[ id ].title === util.configPath + '/' + util.contentLanguage() && id > 0 ) {
						configFullPath = util.configPath + '/' + util.userLanguage();
					}
				}

				var interfaceMessagesUrl = 'https://meta.wikimedia.org/w/index.php?title=' + interfaceMessagesFullPath + '&action=raw';
				var configUrl = 'https://meta.wikimedia.org/w/index.php?title=' + configFullPath + '&action=raw';
				// Get the config for the detected language
				$.when( $.get( interfaceMessagesUrl ), $.get( configUrl ) ).then( function ( interfaceStr, configStr ) {
					var interfaceData = RJSON.parse( interfaceStr[ 0 ] ),
						configData = RJSON.parse( configStr[ 0 ] );

					endorse.config = util.stripWhiteSpace( util.grantType( configData.endorse ) );
					endorse.interfaceMessages = util.stripWhiteSpace( util.grantType( interfaceData.endorse ) );

					endorse.Dialog();
					$( '.wp-addme-button' ).off();
					$( '.wp-addme-button' ).on( 'click', function ( e ) {
						var section = $( this ).attr( 'data-addme-section' ),
							// hack - proper entity encoding is hard in wikitext
							proposal = decodeURIComponent( $( this ).attr( 'data-addme-proposal' ).replace( /\+/g, '_' ) );
						e.preventDefault();

						// Checking if the user is logged in
						if ( !mw.config.get( 'wgUserName' ) ) {
							util.showErrorMessage( 'gadget', 'login' );
							return;
						}

						endorse.section = section;
						endorse.title = mw.config.get( 'wgTitle' ).split( '/' )[ 0 ] + '/' + proposal;
						endorse.Dialog();
					} );

					if ( util.checkFeedbackCookie() ) {
						util.showFeedback( endorse.config, endorse.interfaceMessages );
					}
				} );
			} );
		} else {
			$( '.wp-addme-button' ).hide();
		}
	} );
} );

// </nowiki>