User:Jon Harald Søby/PermissionOTRS.js

Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/** PermissionOTRS.js
// Adds OTRS permission template via the API. Other templates such as {OTRS pending} will be removed.
* @revision 20:10, 7 June 2017 (UTC)
* @license: under the terms of the MIT license
* @authors:
*	Maintainer: [[User:Rillke]]
*	Created by Bryan Tong Minh
*	Amended by [[:de:User:DerHexer]], [[User:Guandalug]], DieBuche, [[User:Steinsplitter]]
*	Partially rewritten by Perhelion
* @required modules: mediawiki.util, jquery.ui, mediawiki.api
// <nowiki>
**/
/*global mediaWiki, jQuery*/
/*jshint curly:false, scripturl:true, forin:false*/
var init = (function ($, mw, OO) {
'use strict';

var user = mw.config.get('wgUserName'),
	uGrp = mw.config.get('wgUserGroups'),
	sLink = '([[MediaWiki:Gadget-PermissionOTRS.js|Script]]) [[COM:OTRS|OTRS]] ',
	page = mw.config.get('wgPageName'),
	ticket,
	edittoken,
	content,
	timestamp,
	reason;

if (mw.config.get('wgNamespaceNumber') !== 6 || $.inArray('autoconfirmed', uGrp) === -1 || !user)
	return;
var editform = (!mw.config.get('wgIsArticle')) ? document.editform : null;

// NEW STUFF
function OTRSdialog(config) {
	OTRSdialog.parent.call(this, config);
}
OO.inheritClass(OTRSdialog, OO.ui.ProcessDialog);
OTRSdialog.static.name = 'otrsdialog';
OTRSdialog.static.title = 'OTRS';
OTRSdialog.static.size = 'small';
OTRSdialog.static.actions = [
	{
		action: 'ok',
		label: mw.msg( 'ooui-dialog-message-accept' ),
		flags: ['primary', 'constructive']
	},
	{
		action: 'cancel',
		label: mw.msg( 'ooui-dialog-message-reject' ),
		flags: 'safe',
	}
];
OTRSdialog.prototype.initialize = function() {
	OTRSdialog.parent.prototype.initialize.apply(this, arguments);
	var OTRSnumber = '';
	var fieldset = new OO.ui.FieldsetLayout({});
	this.OTRSfield = new OO.ui.TextInputWidget({
		value: this.OTRSnumber
	});
	fieldset.addItems([
		new OO.ui.FieldLayout(
			this.OTRSfield,
			{
				align: 'left',
				label: 'Ticket ID (16 digit number):'
			}
		)
	]);
};
// /NEW STUFF

function _prompt(text, OTRS) {
	var OTRSfield = $('#field-otrs'), // there is already a ticket
		name = 'OTRS',
		$select;
	if (OTRSfield.length) // try pre-fill the ticket
		ticket = OTRSfield.attr('title').replace(/[^\n]+(\d{16})$/, '$1');
	else if (editform && /\{\{otrs[ _]received\|id=(\d{16})+[^\}\n]*\}\}/i.test(editform.wpTextbox1.value))
		ticket = RegExp.$1;
	var type = 'errorbox',
		title = (OTRS && OTRS.name) ? OTRS.name : name,
		$reason = $('<br>'),
		$ticket = $('<input>').attr({
				'value': ticket,
				'type': 'text',
				'id': name + 'dialog',
				'maxlength': 23, // for possible pre-phrase "Ticket#"
				'size': 17,
				'class': 'numbersOnly'
			});
			
	if (title === 'OTRSreceived') {
		$select = $('<select>').attr({
			'id': name + 'combo'
		});
		reason = $('<input>').attr({
					'type': 'checkbox',
					'id': name + 'reason'
				}).on('change', function(){
					$select.toggle();
				});
		$reason = $reason.add(reason.add($('<label>').attr({
					'for': name + 'reason',
					'style': 'margin-right:.5em;line-height:1.5em',
				}).text('Give a reason?')
				));
		$.each(['processing', 'licence', 'email'], function (i, el) {
			$select.append($('<option>').text(el));
		});
		$reason = $reason.add($select.hide());
	}
		
	if (!text) {
		title = 'INVALID ID';
		text = 'You must enter a valid 16-digit ticket number.';
	} else if (text === 'FAIL') {
		title = text;
		text = 'No suitable place found to insert template! Please add the template by hand.';
		$ticket = '';
	} else
		type = '';
	text = $('<label>').attr({
			'for': name + 'dialog',
			'style': 'margin-right:0.4em',
			'class': type
		}).text(text);
	$('<div>').append([text, $ticket, $reason]).dialog({
		width: 400,
		dialogClass: 'wikiEditor-toolbar-dialog',
		resizable: false,
		modal: true,
		title: title,
		close: function () {
			$(this).dialog('destroy');
			$(this).remove();
		},
		buttons: [{
				text: 'OK',
				click: function () {
					ticket = ($ticket) ? $.trim($ticket.val()).replace(/^(Ticket)?#/, '') : ''; // cleanup input
					$(this).dialog('close');
					if (ticket) { // Check ticket validity
						if (reason)
							reason = (reason.prop( 'checked'))? $select.find('option:selected').text() : '';
						if (/^\d{16}$/.test(ticket))
							return _getPage(OTRS);
						_prompt('', OTRS);
					}
				}
			}
		]
	});
}

function _addPermission(template, summary) {
	var text = _cleanUp(content),
		text_before = text,
		regP = /( )*\|\s*[Pp]ermission( )*\=\s*([^\n]*\n)/,
		regA = /( )*\|\s*[Aa]uthor( )*\=\s*[^\n]*\n/g; // fallback position, if no permission parameter
	if (!regP.test(text)) {
		if (!regA.test(text))
			return _prompt('FAIL');
		var l = regA.lastIndex,
		s1 = RegExp.$1 || "", // leading whitespaces as indent?
		s2 = (RegExp.$2) ? " " : "", // whitespace on parameter?
		sr = text.substr(l).search(/^\s*(\||\}\})/m); // try to got at template end
		if (sr >= 0) {
			sr += l; // insert to the end of information template
			text = text.substr(0, sr) + s1 + "|permission" + s2 + "=" + s2 + template + "\n" + text.substr(sr);
		} else
			return _prompt('FAIL');
	} else {
		if (text.indexOf(template) > 0)
			return mw.notify('Same ticket already added…', {
				title: 'OTRS fail…',
				type: 'warn'
			});
		text = text.replace(regP, "$1|permission$2=$2" + template + "\n$3");
	}
	if (text_before === text)
		return _prompt('FAIL');
	content = text;
	_savePage(sLink + summary);
}

var PermissionOTRS = function PermissionOTRS() {
	_addPermission('{{PermissionOTRS|id=' + ticket + "|user=" + user + "}}",
		'permission added');
};

var OTRSreceived = function OTRSreceived() {
	_addPermission('{{OTRS received|id=' + ticket +
		'|year={{subst:' +
		'#time:Y}}|month={{subst:' +
		'#time:F}}|day={{subst:' +
		'#time:j}}' + (reason? '|reason=' + reason : '' ) +
		'|user=' + user + '}}',
		'email received but not processed yet');

};

function _cleanUp(text) {
	// ToDo: maybe here could be loaded another general cleanup module!?
	return $.trim(text
		.replace(/\=\= ?Summary ?\=\=/, '=={{int:filedesc}}==')
		.replace(/\=\= ?Licensing ?\=\=/, '=={{int:license-header}}==')
		.replace(/\{\{OTRS[ _\-](pending|received)[^\}\n]*\}\}\s?/ig, '')
		.replace(/\{\{[Nn]o[ _](OTRS[ _])?permission[^\}\n]*\}\}\s*/g, '\n')
		.replace(/\{\{[Dd]elete[^\}\n]*\}\}\s?/g, ''));
}

function _apiFail(code, r) {
	if (!code.indexOf("http")) {
		console.log("HTTP error: " + r.textStatus);
	} else if (code === "ok-but-empty") {
		console.log("Got an empty response from the server");
	} else {
		console.log("API error: " + code);
	}
	mw.notify('There was an error processing your request.\n\t\t\t API-error-code:' +
		code + '\n\n\t\t\t\tPlease try again.', {
		title: 'Error!',
		autoHide: 0,
		type: 'error'
	});
}

function _getPage(OTRS) {
	if (!editform) {
		mw.notify('…request to tag with "' + OTRS.name + '"\n with Ticket#: ' + ticket, {
			title: 'Processing…'
		});
		new mw.Api().get({
			prop: "info|revisions",
			meta: "tokens",
			rvprop: "content|timestamp",
			titles: page
		}).done(function (r) {
			mw.notify('Got page contents…', {
				title: 'Processing…'
			});
			r = r.query;
			var pages = r.pages;
			var tokens = r.tokens;
			if (tokens.hasOwnProperty("csrftoken"))
				edittoken = tokens.csrftoken;
			for (var id in pages) {
				if (pages.hasOwnProperty(id)) {
					pages = pages[id];
					content = pages.revisions[0]['*'];
					timestamp = pages.revisions[0].timestamp;
					return OTRS();
				}
			}
		}).fail(function (code, r) {
			_apiFail(code, r);
		});
	} else {
		content = editform.wpTextbox1.value;
		return OTRS();
	}
}

function doneMsg() {
	return mw.notify('OTRS ticket successfully inserted.', {
		title: 'Done!'
	});
}

function _savePage(summary) {
	if (!editform) {
		new mw.Api().post({
			action: 'edit',
			summary: summary,
			watchlist: 'preferences', // or nochange?
			title: page,
			token: edittoken,
			text: content,
			notminor: 1
		}).done(function () {
			doneMsg().done(function () {
				location.reload();
			});
		}).fail(function (code, r) {
			_apiFail(code, r);
		});
	} else {
		editform.wpMinoredit.checked = false;
		editform.wpTextbox1.value = content;
		var sum = editform.wpSummary;
		sum.value = $.trim(sum.value.replace(summary, "") + " " + summary);
		doneMsg();
	}
}

function dialogOTRS(e, OTRS) {
	e.preventDefault();

	function chkuGrp(b) {
		if (!b)
			return mw.notify('You are not authorized for this function!', {
				title: 'OTRS fail!',
				type: 'warn'
			});
		_prompt('Ticket ID (16 digit number):', OTRS);
	}
	if ($.inArray('OTRS-member', mw.config.get('wgGlobalGroups')) > -1 || $.inArray('sysop', uGrp) > -1)
		chkuGrp(1);
	else // run only for non-admin but 'OTRS-member'
		new mw.Api().get({
			meta: "globaluserinfo",
			guiuser: user,
			guiprop: "groups",
		}).done(function (r) {
			chkuGrp(($.inArray('OTRS-member', r.query.globaluserinfo.groups) > -1));
		}).fail(function (code, r) {
			_apiFail(code, r);
		});
}

var bReceive, bConfirm; // booleans; add link only if needed

$('#mw-hidden-catlinks li').children('a').each(function () {
	if (this.text && !(bReceive && bConfirm)) { // check cats
		if (/^Items with (?:ticket )?OTRS permission confirmed/.test(this.text))
			bConfirm = bReceive = 1;
		else if (/^OTRS received/.test(this.text))
			bReceive = 1;
	}
});
mw.loader.using(['mediawiki.util', 'jquery.ui', 'mediawiki.api'], function () {
	if (!bConfirm) {
		$(mw.util.addPortletLink('p-tb', '#', 'Permission OTRS (BETA)'))
		.on('click', function (e) {
			dialogOTRS(e, PermissionOTRS);
		});
		if (!bReceive)
			$(mw.util.addPortletLink('p-tb', '#', 'OTRS received (BETA)'))
			.on('click', function (e) {
				dialogOTRS(e, OTRSreceived);
			});
	}
});
});

mw.loader.using(['oojs-ui']).then(function() {
	init(jQuery, mediaWiki, OO)
});
// EOF </nowiki>