User:Evad37/FFDcloser/sandbox.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
//global vars
var FFDpath = "Wikipedia:Files for discussion/";

/* == Main function == */
$( function($) {

// Only run in FFD/subpages
        var thispage = mw.config.get( 'wgPageName' );
	if ( !thispage.includes("Files_for_discussion") ) {
	return;
	}

$.getScript('https://en.wikipedia.org/w/index.php?title=User:Evad37/XFDcloserCommon.js&action=raw&ctype=text/javascript', function () {
 // Make sure [[User:Evad37/XFDcloserCommon.js]] is loaded!

 	setSysopStatus();

/* ##### Sandbox usage only (remove when updating main script) ##### */
if ( mw.config.get("wgUserName") === "Evad37" ) {
	isSysop = true;
	console.log("isSysop: " + isSysop);
	FFDpath = "User:Evad37/sandbox/Files for discussion/";
}
/* ##### End of sandbox usage only (remove when updating main script) ##### */

// Add links to actions
	$("h4 > span.mw-headline ").each(function( index ) {
		$(".mw-headline-number", this).prependTo($(this).parent());	//fix for "Auto-number headings" preference
		var file = $(":first-child", this).text();
		if (!file) {                                    // section header wasn't a link
			file = $(this).text();
			$(this).wrapInner("<span></span>");     // wrap with span so it can be styled later
		}
		var filee = mw.util.wikiUrlencode(file);        // "pretty"-encoded

		//Check if already closed
		var discussion_element_class = $(this).parent().next().attr("class");
		if ( discussion_element_class ) {           //only closed discussion have element with any class set
			$(this).parent().addClass("boilerplate ffd vfd xfd-closed"); //add classes to enable hiding of closed section headers
		} else {
			var sectionlink = $(this).next().find("a").not(".mw-editsection-visualeditor").attr('href');
			var editsection = sectionlink.split("section=")[1];
			if ( editsection.search("T")<0 ) {		//if true, we're on the subpage
				nom_page = thispage;
			} else {					//if false, section is transcluded from a subpage
				nom_page = sectionlink.split("title=")[1].split("&")[0];
				editsection = editsection.substr(2); //remove "T-" from section number
			}

			//Check for collapsed list, and pop out a non-visible copy if found
			var $collapsedlist = $(this).parent().next().next().find("dl");
			if ($collapsedlist.length > 0) {
				$collapsedlistitems = $collapsedlist.children().clone();
				$collapsedlistitems.css( "display", "none" )
				$(this).parent().next().append( $collapsedlistitems ).after( "<span style='font-size:85%;padding-left:1em;'>&thinsp;... collapsed list detected (" + $collapsedlistitems.length + " files)</span>" );
			}

			//Fix for linebreaks between list items
			var $siblinglists = $(this).parent().next().nextUntil("p, h4", "dl");
			if ($siblinglists.length > 0) {
				$siblinglistsitems = $siblinglists.children().clone();
				$siblinglistsitems.css( "display", "none" )
				$(this).parent().next().append( $siblinglistsitems);
			}

			//Check if this is a multi-file dicussion
			var $fileslist = $(this).parent().next().children();  //<dd> elements
			var $filesspans = $(":first-child", $fileslist);      //1st <span> of each <dd>
			var $files = $(":first-child", $filesspans).text(); 
			var files = $files.split("File:").slice(1);
			var closing_options;
			if ( files.length > 1) {
				//This is a multi-discussion
				files_e = files.map(function(fname) {
					var f = "File:" + fname;
					var fe = mw.util.wikiUrlencode(f).replace(/,/g,"__COMMA__"); //"pretty"-encode, then encode commas encoded as __COMMA__
					return fe;
				});
				//console.log( files_e );
				closing_options = "Closing options: " + 
"<span style='margin-left:5px;'>[<a style='cursor:pointer;' onclick=multi_process_close_ffd('" + files_e + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "Delete" + "','" + "delete" + "') title='QuickDelete'>qDel</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_close_ffd('" + files_e + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "delete" + "') title='Delete with comments'>Delete</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_process_close_ffd('" + files_e + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "Keep" + "','" + "keep" + "') title='quickKeep'>qKeep</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_close_ffd('" + files_e + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "other" + "') title='Other close with comments'>Other close</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_close_ffd('" + files_e + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "relist" + "') title='Relist discussion'>Relist</a>]</span>";

			} else if (!checkNS(file, 6)) {
				//sanity check
				filee = mw.util.wikiUrlencode($(this).html().replace(/<[^>]*>/g,""));    //this is actually the section header
				closing_options = "No files detected. <span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_close_ffd('" + filee + "','" + nom_page + "','" + filee + "','" + editsection + "','" + "close_only" + "') title='Close with comments'>Close</a>]</span>";
			

			} else {
				//not a multi-discussion
				closing_options = "Closing options: " + 
"<span style='margin-left:5px;'>[<a style='cursor:pointer;' onclick=multi_process_close_ffd('" + filee + "','" + nom_page + "'," + null + ",'" + editsection + "','" + "Delete" + "','" + "delete" + "') title='QuickDelete'>qDel</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=close_ffd('" + filee + "','" + nom_page + "','" + editsection + "','" + "delete" + "') title='Delete with comments'>Delete</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=multi_process_close_ffd('" + filee + "','" + nom_page + "'," + null + ",'" + editsection + "','" + "Keep" + "','" + "keep" + "') title='quickKeep'>qKeep</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=close_ffd('" + filee + "','" + nom_page + "','" + editsection + "','" + "other" + "') title='Other close with comments'>Other close</a>]</span>" + 
"<span style='margin-left:13px;'>[<a style='cursor:pointer;' onclick=close_ffd('" + filee + "','" + nom_page + "','" + editsection + "','" + "relist" + "') title='Relist discussion'>Relist</a>]</span>";
			}
			$(this).append("<span id=XFDClosing_" + filee + " style='font-size:85%;margin-left:13px;font-weight:normal;'>" + closing_options + "</span>");
		}
	});
});

});

/* == Close FFD section (prompting for RESULT commment) == */

//Get comment
function close_ffd(_filename, _pagetitle, _sectionnum, _action) {
	var prompt_text;
	if ( _action === "relist" ) {
		prompt_text = "Enter relist comment (optional)";
	} else {
		prompt_text = "Enter result and comment";
	}
	var result_comment = prompt(prompt_text);
	if ( result_comment !== null ) {
		multi_process_close_ffd(_filename, _pagetitle, null, _sectionnum, result_comment, _action);
	}
}

/* -- Close mulit-FFD section (prompting for RESULT commment) -- */
//Get comment
function multi_close_ffd(_filearray, _pagetitle, _sectionheader, _sectionnum, _action) {
	var prompt_text;
	if ( _action === "relist" ) {
		prompt_text = "Enter relist comment (optional)";
	} else {
		prompt_text = "Enter result and comment";
	}
	var result_comment = prompt(prompt_text);
	if ( result_comment !== null ) {
		multi_process_close_ffd(_filearray, _pagetitle, _sectionheader, _sectionnum, result_comment, _action);
	}
}


/* == Multi-Process close == */
function multi_process_close_ffd(filename, pagetitle, sectionheader, sectionnum, resultcomment, action) {

//-Wikitext used for close
	var editsum_file_or_section, file_or_null, _anchor, ffd_close_top, ffd_close_bottom, ffd_close_editsum;
	if (sectionheader) {
		editsum_file_or_section = "\"" + sectionheader + "\"";
		file_or_null = null;
		_anchor = sectionheader;
	} else {
		editsum_file_or_section = "for [[:" + filename + "]]";
		file_or_null = filename;
		_anchor = filename;
	}

	// Wikitext used for close
	if (isSysop) {
		sig = "~~" + "~~";
	} else {
		sig = "<small>[[Wikipedia:NACD|(non-admin closure)]]</small> ~~" + "~~";
	}
	
	// Figure out if/where bold markup should be added
	var bolding_info = adjustCommentBolding(resultcomment);
	var bold1 = bolding_info[0];
	var bold2 = bolding_info[1];
	resultcomment = bolding_info[2];

	//reset msg_array, deliver started message
	//msg_array = [];
	//console.log("_anchor = " + _anchor);
	deliverMsg( _anchor, "Started closing...", "working" );

	if (action == "relist") {
		//get today's date
		var d = new Date();
		var mmm = getMonthName(d.getUTCMonth());
		todays_subpage = d.getUTCFullYear() + " " + mmm + " " + d.getUTCDate();
		ffd_close_top = "{" + "{subst:ffd top|'''Relisted'''}" + "} on [[" + FFDpath + todays_subpage + "#" + _anchor + "]]}" + "}";
		ffd_close_bottom = "{" + "{subst:ffd bottom}" + "}";
		ffd_close_editsum = "Closing discussion " + editsum_file_or_section + " as relisted on [[" + FFDpath + todays_subpage + "#" + _anchor + "|" + todays_subpage + "]] (using [[User:Evad37/FFDcloser|FFDcloser]])";
	} else {
		ffd_close_top = "{" + "{subst:ffd top|" + bold1 + resultcomment + bold2 + "}" + "} " + sig;
		ffd_close_bottom = "{" + "{subst:ffd bottom}" + "}";
		ffd_close_editsum = "Closing discussion " + editsum_file_or_section + " as " + resultcomment + " (using [[User:Evad37/FFDcloser|FFDcloser]])";
	}

//-Get page content and remove {Closing} template if present
		new mw.Api().get( {
			action: 'query',
			titles: pagetitle,
			prop: [ 'revisions', 'info' ],
			rvprop: 'content',
			indexpageids: 1,
			rawcontinue: ''
		} ).done( function( result, jqXHR ) {
			var p_id = result.query.pageids;
			var p_contents = result.query.pages[ p_id ].revisions[ 0 ][ '*' ];
			var sections_array = p_contents.split("====");
			var section_heading = sections_array[((sectionnum-1)*2-1)];
			var section_contents = sections_array[((sectionnum-1)*2)].replace(/{{closing}}/gi, "");
			var updated_section = "====" + section_heading + "====" + "\n" + ffd_close_top + section_contents + ffd_close_bottom;

//Add relisted discusssions to current day's FFD page
			if (action == "relist") {
				var ffdlinks = section_contents.match(/^:<span class="plainlinks nourlexpansion lx"[^\n]*/gim).join("\n");
				if ( ffdlinks ) {
					updated_section = "====" + section_heading + "====" + "\n" + ffd_close_top + "\n" + ffdlinks + ffd_close_bottom;
				} else {
					updated_section = "====" + section_heading + "====" + "\n" + ffd_close_top + ffd_close_bottom;
				}							
				
				deliverMsg( _anchor, "Adding discussion to today's FFD subpage...", "working" );
				var old_disc = "====" + section_heading + "====" + "\n" + section_contents.replace(/^\n+/,"");
				relist_disc(file_or_null, old_disc, resultcomment, todays_subpage, sectionheader, _anchor);
				
			} else {
				updated_section = "====" + section_heading + "====" + "\n" + ffd_close_top + section_contents + ffd_close_bottom;
			}

//-Perform edit to close FFD discussion
				new mw.Api().postWithToken( 'edit', {
					action: 'edit',
					title: pagetitle,
					section: sectionnum,
					text: updated_section,
					summary: ffd_close_editsum
				} ).done( function( result, jqXHR ) {

//-If successful, mark as closed & move onto next actions (deletes, or update ffd's if relisted, or do old ffd housekeepings)
					var file_array, i, ii, last_one;
					if (sectionheader) {
						file_array = filename.split(","); // filename is a csv string (multiple items)
						for (i = 0; i < file_array.length; i++) {
							file_array[i] = decodeURIComponent(file_array[i]).replace(/__COMMA__/g,","); //unencode commas from __COMMA__
						}
					} else {
						file_array = [decodeURIComponent(filename)];          // filename is a string (single item)
					}
					var file_array_length = file_array.length;

					if ( action == "delete" ) {
//Delete:
						markClosed(_anchor, resultcomment);
						deliverMsg( _anchor, "Deleting...", "working" );
						for (ii = 0; ii < file_array_length; ii++) {
							if (ii == file_array_length-1) {
								last_one = true;
							} else {
								last_one = false;
							}
							delete_file(file_array[ii], "["+"["+pagetitle+"#"+_anchor+"]"+"]", _anchor, last_one);
						}

					} else if ( action == "relist" ) {
//Relist:
						markClosed(_anchor, "Relisted at <a href=//en.wikipedia.org/wiki/" + FFDpath.replace(/\s/g,"_") + todays_subpage.replace(/\s/g,"_") + "#" + _anchor +">" + FFDpath + todays_subpage + "#" + _anchor + "</a>", true);	
						deliverMsg( _anchor, "Adding discussion to today's FFD subpage...", "working" );
						for (ii = 0; ii < file_array.length; ii++) {
							if (ii == file_array_length-1) {
								last_one = true;
							} else {
								last_one = false;
							}
							update_file_ffd(file_array[ii], todays_subpage, _anchor, last_one);
						}

					} else if ( action == "close_only" ) {
//Take no further action
						markClosed(_anchor, resultcomment);
						return;
					} else {
//Keep,other:
						markClosed(_anchor, resultcomment);
						deliverMsg( _anchor, "Updating file description pages...", "working" );
						for (ii = 0; ii < file_array_length; ii++) {
							if (ii == file_array_length-1) {
								last_one = true;
							} else {
								last_one = false;
							}
							make_old_ffd(file_array[ii], resultcomment, pagetitle, sectionheader, _anchor, last_one);
						}
					}
				} ).fail( function( code, result ) {
					var error_details = apifailed( code, result );
					deliverMsg( _anchor, "Could not close discussion " + error_details, "error" );
				} );

		} ).fail( function( code, result ) {
			var error_details = apifailed( code, result );
			deliverMsg( _anchor, "Could not read contents of page " + pagetitle + "; could not close discussion " + error_details, "error" );
		} );
	}

/* == Old ffd housekeeping == */
function make_old_ffd(filename, resultcomment, ffdpage, sectionheader, anchor, lastfile) {

	var page_param, nomdate, filetalkpage, oldffd_wikitext;

	if ( !checkNS(filename, 6) ) {
		deliverMsg( anchor, "\"" + filename + "\" is not in File: namespace, FFDcloser will not edit this page or its talk page", "error", lastfile, null, !lastfile);
		return;
	}

	if (sectionheader) {
		page_param = "|page=" + sectionheader;
	} else {
		page_param = "";
	}

//Add oldffdfull template to file talk
	nomdate = ffdpage.split("Files_for_discussion/")[1].replace(/_/gi," ");           //unencode spaces from _
	filetalkpage = filename.replace("File", "File_talk");
	oldffd_wikitext = "{" + "{oldffdfull|date=" + nomdate + "|result='''" + resultcomment + "'''" + page_param + " }" + "}";

	new mw.Api().postWithToken( 'edit', {
		action: 'edit',
		title: filetalkpage,
		prependtext: oldffd_wikitext,
		summary: "Old FFD – " + nomdate + ": " + resultcomment + " (using [[User:Evad37/FFDcloser|FFDcloser]])"
	} ).done( function( result, jqXHR ) {
		deliverMsg( anchor, "Updated " + filetalkpage, "is_done", null, null, true );

//Get file description page, remove ffd template
		new mw.Api().get( {
			action: 'query',
			titles: filename,
			prop: [ 'revisions', 'info' ],
			rvprop: 'content',
			indexpageids: 1,
			rawcontinue: ''
		} ).done( function( result, jqXHR ) {
//			console.log(result);
			var f_id = result.query.pageids;
			var f_contents = result.query.pages[ f_id ].revisions[ 0 ][ '*' ];
			var updated_filedesc = f_contents.replace(/{{ffd[^}}]*}}/gi, "");

//Perform edit to remove ffd template
				new mw.Api().postWithToken( 'edit', {
					action: 'edit',
					title: filename,
					text: updated_filedesc,
					summary: "FFD closed as " + resultcomment + " (using [[User:Evad37/FFDcloser|FFDcloser]])"
				} ).done( function( result, jqXHR ) {
					deliverMsg( anchor, "Removed {{ffd}} from " + filename, "is_done", null, null, true );
					if (lastfile === true) {
						deliverMsg( anchor, null, null, true ); //remove working message
					}
				} ).fail( function( code, result ) {
					var error_details = apifailed( code, result );
					deliverMsg( anchor, "Could not remove {{ffd}} from " + filename + " " + error_details, "warning", null, null, true );
					if (lastfile === true) {
						deliverMsg( anchor, null, null, true ); //remove working message
					}
				} );

		} ).fail( function( code, result ) {
			var error_details = apifailed( code, result );
			deliverMsg( anchor, filename + " not found; could not remove {{ffd}} " + error_details, "warning", null, null, true );
			if (lastfile === true) {
				deliverMsg( anchor, null, null, true ); //remove working message
			}
		} );

	} ).fail( function( code, result ) {
		var error_details = apifailed( code, result );
		deliverMsg( anchor, "Could not edit " + filetalkpage + " " + error_details, "warning", null, null, true );
		if (lastfile === true) {
			deliverMsg( anchor, null, null, true ); //remove working message
		}
	} );

}


/* == Delete file == */
function delete_file(filename, thereason, anchor, lastfile) {
	if ( !checkNS(filename, 6) ) {
		deliverMsg( anchor, "\"" + filename + "\" is not in File: namespace, FFDcloser will not delete this page", "error", lastfile, null, !lastfile);
		return;
	}
	new mw.Api().postWithToken( 'delete', {
		action: 'delete',
		title: filename,
		reason: thereason,
	} ).done( function( result, jqXHR ) {
		deliverMsg( anchor, "Deleted " + filename, "deleted", null, null, true );
		if (lastfile === true) {
			deliverMsg( anchor, null, null, true ); //remove "Deleting..." message
		}
	} ).fail( function( code, result ) {
		var error_details = apifailed( code, result );
		deliverMsg( anchor, "Could not delete " + filename + " " + error_details, "warning", null, null, true );
		if (lastfile === true) {
			deliverMsg( anchor, null, null, true ); //remove "Deleting..." message
		}
	} );

}


/* == Relist discussion == */
function relist_disc(filename, discussion, relistcomment, todaysdate, sectionheader, anchor) {

	var relist_editsum, relists, num_relists, rc_if_any, newdiscussion;

//Edit summary varies based on if sectionheader is defined or not
	if ( sectionheader ) {
		relist_editsum = "Relisting \"" + sectionheader + "\" (using [[User:Evad37/FFDcloser|FFDcloser]])";
	} else {
		relist_editsum = "Relisting [[:" + filename + "]] (using [[User:Evad37/FFDcloser|FFDcloser]])";
	}

//Check for number of relists, append {relist} template
	relists = discussion.match("[[Wikipedia:Deletion process#Relisting discussions|Relisted]]");
	if ( relists ) {
		num_relists = relists.length + 1;
	} else {
		num_relists = 1;
	}
	if ( relistcomment !== "" ) {
		rc_if_any = relistcomment;
	} else {
		rc_if_any = "";
	}
	newdiscussion = discussion + "\n{" + "{subst:Relist|" + rc_if_any + "|" + num_relists + "}}\n";

//Can append to end of daily subpage
	new mw.Api().postWithToken( 'edit', {
		action: 'edit',
		title: FFDpath + todaysdate,
		appendtext: "\n" + newdiscussion,
		summary: relist_editsum
	} ).done( function( result, jqXHR ) {
		window.open("//en.wikipedia.org/wiki/" + FFDpath + todaysdate, "ffdtoday");
	} ).fail( function( code, result ) {
		var error_details = apifailed( code, result );
		deliverMsg( anchor, "Could not relist discussion " + error_details, "error", true );
	} );

}


/* == Update file ffd template (after relisting) == */
function update_file_ffd(filename, today_subpage, anchor, lastfile) {
	if ( !checkNS(filename, 6) ) {
		deliverMsg( anchor, "\"" + filename + "\" is not in File: namespace, FFDcloser will not edit this page", "error", lastfile, null, !lastfile);
		return;
	}
	new mw.Api().get( {
		action: 'query',
		titles: filename,
		prop: [ 'revisions', 'info' ],
		rvprop: 'content',
		indexpageids: 1,
		rawcontinue: ''
	} ).done( function( result, jqXHR ) {
		var file_id = result.query.pageids;
		var file_contents = result.query.pages[ file_id ].revisions[ 0 ][ '*' ];
		var new_file_contents = file_contents.replace(/{{\s*ffd\s*\|\s*log\s*=\s*[^|}}]*/gi, "{{ffd|log=" + today_subpage );
		new mw.Api().postWithToken( 'edit', {
			action: 'edit',
			title: filename,
			text: new_file_contents,
			summary: "Updating {{ffd}} template: file was relisted (using [[User:Evad37/FFDcloser|FFDcloser]])"
			} ).done( function( result, jqXHR ) {
				deliverMsg( anchor, "Updated {{ffd}} on " + filename, "is_done", null, null, true );
				if (lastfile === true) {
					deliverMsg( anchor, null, null, true ); //remove working message
				}
			} ).fail( function( code, result ) {
				var error_details = apifailed( code, result );
				deliverMsg( anchor, "Could not update {{ffd}} on " + filename + " " + error_details, "warning", null, null, true );
				if (lastfile === true) {
					deliverMsg( anchor, null, null, true ); //remove working message
				}
			} );
	} ).fail( function( code, result ) {
		var error_details = apifailed( code, result );
		deliverMsg( anchor, filename + " not found; could not update {{ffd}} " + error_details, "warning", null, null, true );
		if (lastfile === true) {
			deliverMsg( anchor, null, null, true ); //remove working message
		}
	} );

}
// </nowiki>