//@ts-check
/**
* @author ]
*
* Skrypt do nominacji artykułów do wyróżnień AnM, DA i LnM.
*
* <nowiki>
*/
(function ($, mw) {
var wzwMSG = {
justificationLabel: 'Uzasadnienie',
justificationHelp: 'Opisz, dlaczego, Twoim zdaniem, artykuł nadaje się do wybranego wyróżnienia (a w przypadku zgłoszenia do weryfikacji – dlaczego się nie nadaje). Twój podpis na stronie nominacji zotanie wstawiony automatycznie.',
authorsLabel: 'Główni autorzy',
authorsHelp: '<span>Tutaj możesz wpisać nazwy użytkowników, którzy mają zostać powiadomieni o nominacji. Zatwierdź klawiszem Tab lub Enter. Lista autorów znajduje się w <a href="/w/index.php?title=$1&action=history" target="_blank">historii strony</a>. Możesz też posłużyć się <a href="https://xtools.wmflabs.org/articleinfo/pl.wikipedia.org/$1" target="_blank">szczegółowymi statystykami</a>.</span>',
wikiprojectsLabel: 'Wikiprojekty do powiadomienia',
wikiprojectsHelp: 'Powiadomienie o nominacji zostanie umieszczone na stronach wyżej wymienionych wikiprojektów.',
justificationEmpty: 'Pole „Uzasadnienie” nie może być puste.',
nominationCreateSummary: 'Nowe zgłoszenie do wyróżnienia',
nominationCreateSummaryRevoke: 'Nowe zgłoszenie do weryfikacji wyróżnienia',
nominationMarker: 'Uzasadnienie:\n',
lobbyEditSummary: 'Dodano nowe zgłoszenie: ]',
lobbyEditSummaryRevoke: 'Dodano nowe zgłoszenie do weryfikacji: ]',
authorTalkSubject: 'Artykuł ] został zgłoszony do wyróżnienia',
authorTalkSubjectRevoke: 'Artykuł ] został zgłoszony do weryfikacji wyróżnienia',
authorTalkMessage: '$1\nPozdrawiam, ~~~~',
articleEditSummary: 'Artykuł został zgłoszony do wyróżnienia: ]',
articleEditSummaryRevoke: 'Artykuł został zgłoszony do weryfikacji wyróżnienia: ]',
wikiprojectMessage: '$1\n~~~~',
wikiprojectMarker: '<!-- Nowe nominacje wstawiaj poniżej tej linii. Nie zmieniaj ani nie kasuj tej linii. -->',
wikiprojectBeforeMarker: '<!-- Poniższa sekcja została utworzona przez gadżet „Zgłoś do wyróżnienia”. Możesz ją swobodnie przenosić, a także zmieniać tekst nagłówka. -->\n=== Propozycje wyróżnień ===\n',
wikiprojectNotifySummary: 'Artykuł ] został zgłoszony do wyróżnienia',
wikiprojectNotifySummaryRevoke: 'Artykuł ] został zgłoszony do weryfikacji wyróżnienia',
invalidTitle: 'Strona nominacji miałaby niepoprawny tytuł. Gadżet nie jest w stanie obsłużyć takiej sytuacji.',
canFindMarkerInLobby: '<!-- Gadżet nie był w stanie zlokalizować właściwego miejsca do wstawienia tego zgłoszenia. -->',
errorCantMakeTitle: 'Nie udało się określić tytułu dla strony nominacji. $1',
errorCantCreatePage: 'Nie udało się utworzyć strony nominacji. $1',
errorCantAddToLobby: 'Strona nominacji została utworzona, ale nie udało się osadzić zgłoszenia na stronie propozycji. $1',
errorCantAddTemplate: 'Nominacja została wykonana, ale nie udało się oznaczyć artykułu szablonem propozycji wyróżnienia. Nie powiadomiono też autorów artykułu. $1',
errorCantNotifyUsers: 'Nominacja została wykonana, ale nie udało się powiadomić użytkowników. $1',
errorCantNotifyWikiprojects: 'Nominacja została wykonana, ale nie udało się powiadomić wikiprojektów. $1',
apiFormatError: 'Serwer odpowiedział w nieprawidłowym formacie. Szczegóły błędu być może znajdują się w konsoli w narzędziach deweloperskich (zazwyczaj dostępnych po naciśnięciu F12).',
};
// Defines who is regarded as a 'main author' of an article.
// In the last 60 days, the user did changes whose magnitudes sum to over 5000 bytes.
var AUTHORS = {
days: 60,
totalEditSize: 5000
};
/** @type {NominationTypeWZW} */
var NOMINATION_TYPES = [
{
sortOrder: 1,
label: 'Dobry Artykuł',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Propozycja_DA.svg/15px-Propozycja_DA.svg.png',
lobbyMarker: /(== Propozycje ==\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do Dobrych Artykułów',
preload: 'Wikipedia:Propozycje do Dobrych Artykułów/preload',
talkTemplate: '{{Propozycja wyróżnienia|DA$1|artykuł=$2}}',
template: '{{Propozycja wyróżnienia|DA$1}}',
transform: function (content) {
return content.replace('<!-- Powód, dlaczego uważasz, że zasługuje na miano Dobrego Artykułu. (Ewentualnie pewne mankamenty, które nie pozwalają, aby hasło mogło być uznane za medalowe).-->', '');
},
isRevoke: false,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Artykuł nie może być zgłoszony do DA, jeśli posiada już jakieś wyróżnienie lub jest nominowany do jakiegokolwiek
if(categories.indexOf('Artykuły zgłoszone do Dobrych Artykułów') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do List na Medal') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do Artykułów na Medal') !== -1) return false;
if(categories.indexOf('Dobre Artykuły') !== -1) return false;
if(categories.indexOf('Listy na Medal') !== -1) return false;
if(categories.indexOf('Artykuły na Medal') !== -1) return false;
return true;
}
},
{
sortOrder: 4.1,
label: 'Dobry Artykuł – weryfikacja',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Odebranie_DA_orange.svg/15px-Odebranie_DA_orange.svg.png',
lobbyMarker: /(\{\{\/weryfikacja\}\}\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do Dobrych Artykułów',
preload: 'Wikipedia:Propozycje do Dobrych Artykułów/weryfikacja/preload',
talkTemplate: '{{Weryfikacja wyróżnienia|DA$1|artykuł=$2}}',
template: '{{Weryfikacja wyróżnienia|DA$1}}',
titleSuffix: '/weryfikacja',
isRevoke: true,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Do weryfikacji DA można zgłaszać tylko DA
return categories.indexOf('Dobre Artykuły') !== -1;
}
},
{
sortOrder: 2,
label: 'Lista na Medal',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Propozycja_LnM-2.svg/15px-Propozycja_LnM-2.svg.png',
lobbyMarker: /(== Propozycje ==\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do List na Medal',
preload: 'Wikipedia:Propozycje do List na Medal/preload',
talkTemplate: '{{Propozycja wyróżnienia|LnM$1|artykuł=$2}}',
template: '{{Propozycja wyróżnienia|LnM$1}}',
isRevoke: false,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Artykuł nie może być zgłoszony do LnM, jeśli posiada już wyróżnienie LnM/AnM lub jest nominowany do jakiegokolwiek
if(categories.indexOf('Artykuły zgłoszone do Dobrych Artykułów') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do List na Medal') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do Artykułów na Medal') !== -1) return false;
if(categories.indexOf('Listy na Medal') !== -1) return false;
if(categories.indexOf('Artykuły na Medal') !== -1) return false;
return true;
}
},
{
sortOrder: 4.2,
label: 'Lista na Medal – weryfikacja',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Odbieranie_LnM.svg/15px-Odbieranie_LnM.svg.png',
lobbyMarker: /(\{\{\/weryfikacja\}\}\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do List na Medal',
preload: 'Wikipedia:Propozycje do List na Medal/weryfikacja/preload',
talkTemplate: '{{Weryfikacja wyróżnienia|LnM$1|artykuł=$2}}',
template: '{{Weryfikacja wyróżnienia|LnM$1}}',
titleSuffix: '/weryfikacja',
isRevoke: true,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Do weryfikacji LnM można zgłaszać tylko LnM
return categories.indexOf('Listy na Medal') !== -1;
}
},
{
sortOrder: 3,
label: 'Artykuł na Medal',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Wikimedal_POL.svg/12px-Wikimedal_POL.svg.png',
lobbyMarker: /(== Propozycje ==\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do Artykułów na Medal',
preload: 'Wikipedia:Propozycje do Artykułów na Medal/preload',
talkTemplate: '{{Propozycja wyróżnienia|AnM$1|artykuł=$2}}',
template: '{{Propozycja wyróżnienia|AnM$1}}',
isRevoke: false,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Artykuł nie może być zgłoszony do AnM, jeśli posiada już wyróżnienie LnM/AnM lub jest nominowany do jakiegokolwiek
if(categories.indexOf('Artykuły zgłoszone do Dobrych Artykułów') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do List na Medal') !== -1) return false;
if(categories.indexOf('Artykuły zgłoszone do Artykułów na Medal') !== -1) return false;
if(categories.indexOf('Listy na Medal') !== -1) return false;
if(categories.indexOf('Artykuły na Medal') !== -1) return false;
return true;
}
},
{
sortOrder: 4.3,
label: 'Artykuł na Medal – weryfikacja',
icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Omedal_orange.svg/15px-Omedal_orange.svg.png',
lobbyMarker: /(\{\{\/weryfikacja\}\}\n<!-- Nowe propozycje umieszczaj pod tą linią.*?-->\n\n?)/i,
rootPage: 'Wikipedia:Propozycje do Artykułów na Medal',
preload: 'Wikipedia:Propozycje do Artykułów na Medal/weryfikacja/preload',
talkTemplate: '{{Weryfikacja wyróżnienia|AnM$1|artykuł=$2}}',
template: '{{Weryfikacja wyróżnienia|AnM$1}}',
titleSuffix: '/weryfikacja',
isRevoke: true,
createView: createView,
submit: submit,
validateForm: validateForm,
isApplicable: function (categories) {
// Do weryfikacji AnM można zgłaszać tylko AnM
return categories.indexOf('Artykuły na Medal') !== -1;
}
}
];
var viewPanel;
var justificationInput;
var authorsInput;
var wikiprojectsInput;
/**
* Creates a panel with the necessary controls
* @returns {OO.ui.PanelLayout}
*/
function createView() {
if(viewPanel) return viewPanel;
viewPanel = new OO.ui.PanelLayout({
padded: false,
expanded: false,
});
var fieldset = new OO.ui.FieldsetLayout({});
viewPanel.$element.append(fieldset.$element);
// The "Justification" field
justificationInput = new OO.ui.MultilineTextInputWidget({
rows: 5,
indicator: 'required'
});
var justificationField = new OO.ui.FieldLayout(justificationInput, {
label: wzwMSG.justificationLabel,
align: 'top',
help: wzwMSG.justificationHelp,
helpInline: true
});
var pageName = mw.config.get('wgPageName');
// The "Main authors" field
authorsInput = new OO.ui.TagMultiselectWidget({
allowArbitrary: true
});
var authorsField = new OO.ui.FieldLayout(authorsInput, {
label: wzwMSG.authorsLabel,
align: 'top',
help: $(mw.format(wzwMSG.authorsHelp, pageName)),
helpInline: true
});
// The "Wikiprojects field"
wikiprojectsInput = new OO.ui.MenuTagMultiselectWidget({
inputPosition: 'inline',
options:
});
var wikiprojectsField = new OO.ui.FieldLayout(wikiprojectsInput, {
label: wzwMSG.wikiprojectsLabel,
align: 'top',
help: wzwMSG.wikiprojectsHelp,
helpInline: true
});
fieldset.addItems([
justificationField,
authorsField,
wikiprojectsField
]);
viewPanel.$element.append(fieldset.$element);
getMainAuthors(pageName).then(function (authors) {
var currentUser = mw.config.get('wgUserName');
var usernames = Object.keys(authors);
usernames.forEach(function (name) {
if(name == currentUser) return;
authorsInput.addTag(name);
});
}).fail(function () { });
// The wikiprojects select is populated asynchronously
gadget.getWikiprojects().then(function (result) {
var wikiprojectOptions = result.wikiprojects.map(
function (wikiproject) {
return {
data: wikiproject.page,
label: wikiproject.name
};
}
);
wikiprojectsInput.addOptions(wikiprojectOptions);
});
return viewPanel;
}
/**
* Validates the form
* @returns {{isValid: boolean, messages: string}}
*/
function validateForm() {
var justification = justificationInput.getValue();
if(!justification || justification.length < 10) {
return {
isValid: false,
messages:
};
}
return {
isValid: true,
messages:
};
}
/**
* Submits the form and starts the nomination
*
* @param {NominationTypeWZW} nominationType The nomination type
* @returns {OO.ui.Process}
*/
function submit(nominationType) {
var pageName = mw.config.get('wgPageName');
var justification = justificationInput.getValue();
var authors = authorsInput.getValue();
var wikiprojects = wikiprojectsInput.getValue();
return nominate(pageName, nominationType, justification, authors, wikiprojects);
}
/**
* Returns a promise resolving to the list of main article authors
* @param {string} title The title of the page
* @returns {JQuery.Promise<{: number}, string>}
*/
function getMainAuthors(title) {
var deferred = $.Deferred();
getRevisions(title, AUTHORS.days).then(function (revisions) {
var contributors = {};
for(var i = 0; i < revisions.length - 1; i++) {
var revision = revisions;
if(revision.anon !== undefined) continue;
if(revision.user === 'userhidden') continue;
var diff = Math.abs(revision.size - revisions.size);
if(contributors === undefined)
contributors = 0;
contributors += diff;
}
Object.keys(contributors).forEach(function (user) {
if(contributors >= AUTHORS.totalEditSize) return;
delete contributors;
});
deferred.resolve(contributors);
}).fail(function (reason) {
deferred.reject(reason);
});
return deferred.promise();
}
/**
* Returns list of revisions that were made over the specified period
* @param {string} title The page
* @param {number} days How many days to take into account
* @returns {JQuery.Promise<any, string>}
*/
function getRevisions(title, days) {
var deferred = $.Deferred();
var limitDate = new Date();
limitDate.setDate(limitDate.getDate() - days);
var params = {
'action': 'query',
'format': 'json',
'prop': 'revisions',
'titles': title,
'rvprop': ,
'rvlimit': 'max',
'rvend': limitDate.toISOString()
};
zdw.api.get(params).then(function (data) {
var revisions = data.query.pages.revisions || ;
deferred.resolve(revisions);
}).fail(function (code, response) {
var message = zdw.api.getErrorMessage(response).text();
deferred.reject(message);
});
return deferred.promise();
}
/**
* Invokes all the steps in order to nominate the article
* @param {string} pageTitle The title of the page
* @param {NominationTypeWZW} nominationType Data about the nomination type
* @param {string} justification The reason why to feature the article
* @param {string} usersToNotify List of usernames whom to notify
* @param {string} wikiprojectsToNotify List of wikiproject pages to put a notice
* @returns {OO.ui.Process}
*/
function nominate(pageTitle, nominationType, justification, usersToNotify, wikiprojectsToNotify) {
// Will be set by the process
var nominationPage = '';
var nominationNumber = 0;
// Signature is hard-coded on the preload page
justification = justification.replace(/~{3,5}/g, '');
// Prepare the 'readable' page title with underscores converted to spaces
var pageTitleReadable = pageTitle.replace(/_/g, ' ');
return new OO.ui.Process(
function () {
return errorIfRejected(
makeNominationTitle(nominationType.rootPage, pageTitle, nominationType.titleSuffix)
.then(function (result) {
nominationPage = result.title;
nominationNumber = result.number;
}),
wzwMSG.errorCantMakeTitle
);
})
.next(function () {
var summary = wzwMSG.nominationCreateSummary;
if(nominationType.isRevoke) summary = wzwMSG.nominationCreateSummaryRevoke;
return errorIfRejected(
createNominationPage(nominationType.preload, justification, nominationPage, wzwMSG.nominationMarker, summary, nominationType.transform),
wzwMSG.errorCantCreatePage
);
})
.next(function () {
var summary = wzwMSG.lobbyEditSummary;
if(nominationType.isRevoke) summary = wzwMSG.lobbyEditSummaryRevoke;
return errorIfRejected(
addNominationToLobby(nominationType.rootPage, nominationPage, nominationType.lobbyMarker, summary),
wzwMSG.errorCantAddToLobby,
{ recoverable: false }
);
})
.next(function () {
var nominationSuffix = '|' + nominationNumber;
if(nominationNumber == 1) nominationSuffix = '';
var template = mw.format(nominationType.template, nominationSuffix) + '\n';
var summary = wzwMSG.articleEditSummary;
if(nominationType.isRevoke) summary = wzwMSG.articleEditSummaryRevoke;
summary = mw.format(summary, nominationPage, nominationType.label);
return errorIfRejected(
prependPage(pageTitle, template, summary),
wzwMSG.errorCantAddTemplate,
{ recoverable: false }
);
})
.next(function () {
var nominationSuffix = '|' + nominationNumber;
if(nominationNumber == 1) nominationSuffix = '';
var template = mw.format(nominationType.talkTemplate, nominationSuffix, pageTitleReadable);
var subject = wzwMSG.authorTalkSubject;
if(nominationType.isRevoke) subject = wzwMSG.authorTalkSubjectRevoke;
subject = mw.format(subject, nominationPage, pageTitleReadable);
var message = mw.format(wzwMSG.authorTalkMessage, template);
return errorIfRejected(
notifyUsers(usersToNotify, subject, message),
wzwMSG.errorCantNotifyUsers,
{ recoverable: false }
);
})
.next(function () {
var nominationSuffix = '|' + nominationNumber;
if(nominationNumber == 1) nominationSuffix = '';
var template = mw.format(nominationType.talkTemplate, nominationSuffix, pageTitleReadable);
var summary = wzwMSG.wikiprojectNotifySummary;
if(nominationType.isRevoke) summary = wzwMSG.wikiprojectNotifySummaryRevoke;
summary = mw.format(summary, nominationPage, pageTitleReadable);
return errorIfRejected(
notifyWikiprojects(wikiprojectsToNotify, summary, template),
wzwMSG.errorCantNotifyWikiprojects,
{ recoverable: false }
);
});
}
/**
* Makes the nomination page title, regarding any potential previous nominations
* @param {string} nominationRootPage The lobby of nominations of the given type
* @param {string} articleName The article name
* @param {string} The suffix to be added to the title
* @returns {JQuery.Promise<{title: string, number: number}, ApiError>}
*/
function makeNominationTitle(nominationRootPage, articleName, titleSuffix) {
/** @type {JQuery.Deferred<{title: string, number: number}, ApiError>} */
var deferred = $.Deferred();
titleSuffix = titleSuffix || '';
// Get list of all pages with the given prefix
var firstPage = nominationRootPage + '/' + articleName + titleSuffix;
var title = mw.Title.newFromText(firstPage);
if(!title) {
deferred.reject({ code: 'invalidtitle', message: wzwMSG.invalidTitle });
return deferred.promise();
}
// Normalize the title
firstPage = title.getPrefixedText();
var params = {
action: 'query',
list: 'allpages',
apnamespace: title.getNamespaceId(),
apprefix: title.getMain(),
aplimit: 'max'
};
zdw.api.get(params).then(
function (data) {
var pages = data.query.allpages;
var titles = pages.map(function (page) { return page.title; });
if(titles.indexOf(firstPage) === -1) return deferred.resolve({ title: firstPage, number: 1 });
var i = 2;
while(true) {
var seqTitle = firstPage + '/' + i;
if(titles.indexOf(seqTitle) === -1) return deferred.resolve({ title: seqTitle, number: i });
i++;
}
},
function (code, response) {
var message = zdw.api.getErrorMessage(response).text();
deferred.reject({ code: code, message: message });
}
);
return deferred.promise();
}
/**
* Creates a page with nomination
* @param {string} templatePage The page with template of the nomination
* @param {string} justification Why should it be featured
* @param {string} nominationPage Where to put the nomination page
* @param {string} marker Where to put the justification
* @param {string} editSummary The edit summary to use
* @param {((content: string) => string) | undefined} transform An additional transformation to apply to the nomination page
* @returns {JQuery.Promise<void, ApiError>}
*/
function createNominationPage(templatePage, justification, nominationPage, marker, editSummary, transform) {
/** @type {JQuery.Deferred<any, ApiError>} */
var deferred = $.Deferred();
var preloadParams = {
'action': 'query',
'prop': 'revisions',
'titles': templatePage,
'rvprop': 'content',
'rvslots': 'main',
'rvlimit': 1
};
zdw.api.get(preloadParams).then(function (response) {
var page = response.query.pages;
var content = page.revisions.slots.main.content;
content = stripIncludeTags(content);
content = content.replace(marker, marker + justification + ' ');
if(transform) content = transform(content);
zdw.api.create(nominationPage, { summary: editSummary }, content)
.then(
deferred.resolve,
function (code, response) {
var message = zdw.api.getErrorMessage(response).text();
deferred.reject({ code: code, message: message });
}
);
}, function (code, response) {
var message = zdw.api.getErrorMessage(response).text();
deferred.reject({ code: code, message: message });
});
return deferred;
}
/**
* Adds a nomination to the lobby
* @param {string} lobbyPage The title of the lobby page
* @param {string} nominationPage The page with nomination
* @param {RegExp} marker Where to put the new nomination
* @param {string} editSummary The edit summary to use
* @returns {JQuery.Promise<void, ApiError>}
*/
function addNominationToLobby(lobbyPage, nominationPage, marker, editSummary) {
// Cut the prefix and make the title relative in template
var nominationPageTemplate = nominationPage;
if(nominationPage.indexOf(lobbyPage + '/') == 0) {
nominationPageTemplate = nominationPage.substring(lobbyPage.length);
}
return repeatOnEditConflict(
appendTextToMarker,
[
lobbyPage,
marker,
'{{' + nominationPageTemplate + '}}\n',
mw.format(editSummary, nominationPage),
wzwMSG.canFindMarkerInLobby
],
3 // 3 attempts should be sufficient
);
}
/**
* Posts a message to the given users' talk pages
* @param {string} users Array of users to notify
* @param {string} subject The message subject
* @param {string} message The message to post
* @returns {JQuery.Promise<void, string>}
*/
function notifyUsers(users, subject, message) {
var promises = ;
for(var i = 0; i < users.length; i++) {
var userTalk = new mw.Title('User talk:' + users);
var promise = postTopic(userTalk, subject, message);
promises.push(promise);
}
return $.when.apply($, promises);
}
/**
* Posts a message to the given wikiproject talk pages
* @param {string} wikiprojects Array of wikiprojects to notify
* @param {string} editSummary The edit summary
* @param {string} message The message to post
* @returns {JQuery.Promise<void, ApiError>}
*/
function notifyWikiprojects(wikiprojects, editSummary, message){
var promises = ;
for(var i = 0; i < wikiprojects.length; i++) {
var wikiprojectPage = wikiprojects;
var promise = repeatOnEditConflict(
appendTextToMarker,
[
wikiprojectPage,
wzwMSG.wikiprojectMarker,
message,
editSummary,
wzwMSG.wikiprojectBeforeMarker
],
3 // 3 attempts should be sufficient
)
promises.push(promise);
}
// Resolve our promise only after all edits are finished
return $.when.apply($, promises);
}
/**
* Invokes the function and retries it in case of an edit conflict.
* Use only when it makes sense to redo the operation with the same arguments.
* @param {(...args: TArgs) => JQuery.Promise<TSuccess, TFail>} func The function to invoke
* @param {TArgs} args The function arguments
* @param {number} attemptCount The number of attempts to perform
* @returns {JQuery.Promise<TSuccess, TFail>}
* @template TSuccess
* @template {{ code: string }} TFail
* @template {any} TArgs
*/
function repeatOnEditConflict(func, args, attemptCount) {
var deferred = $.Deferred();
function attempt() {
return func.apply(null, args)
.then(
deferred.resolve,
function (/** @type{TFail} */ error) {
if(error.code === 'editconflict' && attemptCount > 1) {
attemptCount--;
return attempt();
}
deferred.reject(error);
}
);
}
attempt();
return deferred.promise();
}
/**
* Strips <includeonly> tags and removes all the text that's between <noinclude></noinclude>
* @param {string} content The text
* @returns {string}
*/
function stripIncludeTags(content) {
content = content.replace(/<noinclude>.*?<\/noinclude>/gi, '');
content = content.replace(/<includeonly>/gi, '');
content = content.replace(/<\/includeonly>/gi, '');
return content;
}
/**
* Posts a new topic to the specified page
*
* @param {mw.Title} title Where to post the message
* @param {string} subject The subject of the message
* @param {string} message The message to post
* @returns {JQuery.Promise<any, string>}
*/
function postTopic(title, subject, message){
var deferred = $.Deferred();
mw.messagePoster.factory.create(title).then(function (poster) {
poster.post(subject, message).then(function (data) {
// Resolve the promise if the post was successful
// Usually it's going to be a normal edit
// but it may happen that the talk page is a Flow page - check both
if(data.edit && data.edit.result === 'Success') return deferred.resolve();
if(data.flow && data.flow.status == 'ok') return deferred.resolve();
deferred.reject(wzwMSG.apiFormatError);
}).fail(function (primaryError, secondaryError, details) {
console.error(primaryError, secondaryError, details);
if(primaryError != 'api-fail') return deferred.reject(details.toString());
var errorInfo = zdw.api.getErrorMessage(details).text();
deferred.reject(errorInfo);
});
});
return deferred.promise();
}
/**
* Prepends the page with the given text
* @param {string} page The page to edit
* @param {string} text The text to insert at the beginning of the page
* @param {string} editSummary The edit summary
* @returns {JQuery.Promise<void, ApiError>}
*/
function prependPage(page, text, editSummary) {
/** @type {JQuery.Deferred<any, ApiError>} */
var deferred = $.Deferred();
var params = {
action: 'edit',
title: page,
prependtext: text,
summary: editSummary
};
zdw.api.postWithEditToken(params).then(
deferred.resolve,
function (code, response) {
var message = zdw.api.getErrorMessage(response).text();
deferred.reject({ code: code, message: message });
}
);
return deferred.promise();
}
/**
* Finds a specific marker on the page and appends to it
* @param {string} pageTitle The page to edit
* @param {string | RegExp} marker The marker to be found
* @param {string} textToAppend String that will be inserted just after the marker
* @param {string} editSummary Summary of the edit
* @param {string} If the marker is not found, this text will be appended to the page content, followed by the `marker` and `textToAppend`.
* @returns {JQuery.Promise<void, ApiError>}
*/
function appendTextToMarker(pageTitle, marker, textToAppend, editSummary, beforeMarker) {
var deferred = $.Deferred();
beforeMarker = beforeMarker || '';
zdw.api.edit(pageTitle, function (revision) {
// Replace the marker with itself + the new text
var replacement = (typeof marker == 'string') ?
(marker + '\n' + textToAppend)
: ('$1' + textToAppend);
var newContent = revision.content.replace(
marker,
replacement
);
// Check if the marker was found; if not, append the text to the end
if(newContent == revision.content) {
newContent += '\n' + beforeMarker;
if (typeof marker == 'string') newContent += marker + '\n';
newContent += textToAppend;
}
return {
text: newContent,
summary: editSummary
};
}).then(
deferred.resolve,
function (code, result) {
var errorMessage = zdw.api.getErrorMessage(result).text();
return deferred.reject({ code: code, message: errorMessage });
}
);
return deferred.promise();
}
/**
* Wraps a promise so that if it's rejected, the new promise will be rejected with
* OO.ui.Error as a reason.
* @template T
* @param {JQuery.Promise<T, (string | { message: string })>} promise A promise to wrap
* @param {string} A messageTempate. `$1` will be replaced with the original promise's rejection reason
* @param {object} Options to pass to OO.ui.Error
* @returns {JQuery.Promise<T, OO.ui.Error>}
*/
function errorIfRejected(promise, messageTemplate, errorOptions) {
var deferred = $.Deferred();
promise.then(function (result) {
deferred.resolve(result);
}, function (reason) {
if (typeof reason === 'object') {
reason = reason.message;
}
var message = mw.format(messageTemplate || '$1', reason);
deferred.reject(new OO.ui.Error(message, errorOptions));
});
return deferred.promise();
}
// Attach to the main script
window.zdw = window.zdw || {};
zdw.nominationTypes = zdw.nominationTypes || ;
zdw.nominationTypes.push.apply(zdw.nominationTypes, NOMINATION_TYPES);
})($, mw);
// </nowiki>