MediaWiki:Gadget-qualityArticles.js
Возможно, этот код документирован.
// <nowiki>
/*
Скрипт автоматизирует ряд действий для проекта Добротные статьи:
1. Добавляет ссылку "Номинировать в ДС" в группе "Инструменты" слева -> номинация статей на ВП:КДС.
См. addButtonsNominate
2. Добавляет ссылку "Номинировать на лишение статуса ДС" в группе "Инструменты" слева -> номинация на снятие статуса ДС.
См. addNominateToCancellationButtons
3. На подстраницах ВП:КДС добавляет кнопки За, Против и Комментарий,
а для избрирающих - кнопки Избрать, Избрать + в ХС, Отказать, Отказать + в ХС/ИС.
См. addButtonsDiscussion
4. На страницу ВП:КДС добавляет кнопку для архивирования завершенных обсуждений.
См. addButtonsArchive
5. На страницы добротных статей добавляет кнопку "Категории ДС" для изменения категорий.
См. addButtonsChangeCategories
Список избирающих следует заполнять в [[MediaWiki:Gadget-qualityArticles.json]]
Таблица на странице ВП:КДС форматируется специальным модулем: [[Модуль:Добротные статьи]].
Этот модуль проставляет маркер "ruWikiQualityCandatatesToArchivButton" для гаджето-кнопки "Отправить в архив".
Developers: актуальная разработческая версия скрипта здесь: Участник:Нирваньчик/ds.js
*/
var RuWikiQualityArticles = function() {
var ruWikiQualityArticles = this;
var summarySuffix = RuWikiQualityArticles.summarySuffix;
// Википедия:Кандидаты в добротные статьи/Список
var PAGE_ID_CANDIDATES = 5020826;
// Википедия:Добротные статьи/Категории
var PAGE_ID_CATEGORIES = 5027966;
var PAGE_PREFIX_LIST = 'Википедия:Добротные статьи/Список/';
var PAGE_PREFIX_KDS = 'Википедия:Кандидаты в добротные статьи/';
var PAGE_CANCEL_DISC = this.PAGE_CANCEL_DISC = 'Проект:Добротные статьи/К лишению статуса';
var PAGE_PRIVILEGED_USERS = './qualityArticles.json';
var categories = null;
var COMMONS_UPLOAD = 'https://xn--b1aeclack5b4j.su/save/png/wikipedia/commons/thumb';
var T_ERR = ' Ошибка!';
var T_OK = ' Успешно.';
var T_SIGN = '~' + '~' + '~' + '~';
var T_SIGN_A = ' — ' + T_SIGN;
var T_FINISHED_OK = 'Все правки завершены успешно';
var T_FINISHED_ERR = 'Завершено с ошибками. Просмотрите ваш вклад и попробуйте доделать нужные правки вручную.';
var DEBUG = false;
if (DEBUG === true) {
PAGE_ID_CANDIDATES = 7352333;
PAGE_PREFIX_KDS = 'Участник:Нирваньчик/Кандидаты в добротные статьи/';
PAGE_PREFIX_LIST = 'Участник:Нирваньчик/Добротные статьи/Список/';
PAGE_PRIVILEGED_USERS = 'Участник:Нирваньчик/Gadget-qualityArticles-testadmin.json';
}
var notifyOptions = this.notifyOptions = {
autoHide: true,
tag: 'QA-Gadget',
};
var notifyOptOk = notifyOptions;
// Ошибки показываем как ошибки - в красной рамке,
// и не убираем, чтобы юзер успел прочитать и закрыть сам.
var notifyOptError = {
autoHide: false,
type: 'error',
};
var getFirstObjectValue = RuWikiQualityArticles.getFirstObjectValue;
var funcArray = function(size) {
var funcEmpty = function() {
alert( 'Empty function call (error)' );
};
var funcs = new Array( size ).map( function( x, i ) {
return funcEmpty;
} );
return funcs;
};
function getPrivilegedUsersOld(callback) {
$.getJSON( mw.util.wikiScript(), {
title: PAGE_PRIVILEGED_USERS,
action: 'raw'
} ).done( function (ans) {
var officerList = ans;
var userName = mw.config.get( 'wgUserName' );
callback( officerList.includes(userName) );
} );
}
this.addButtonsDiscussion = function() {
// check if this is nominations page
if ( $( ".ruWikiQualityArticlesNavigation" ).length != 1 ) {
return;
}
if (DEBUG === true) {
// This mode used with importScript() method of loading gadget.
// In this mode "require" insruction is not available: ReferenceError: require is not defined
getPrivilegedUsersOld(addButtonsDiscussionImpl);
} else {
var officerList = require( PAGE_PRIVILEGED_USERS );
var isOfficer = officerList.includes( mw.config.get( 'wgUserName' ) );
addButtonsDiscussionImpl( isOfficer );
}
};
var addButtonsDiscussionImpl = function(isOfficer) {
addButtonsHtmlToKDS( isOfficer );
addOpinionForm();
if ( isOfficer ) {
addSummaryForm();
}
};
var addButtonsHtmlToKDS = function(officer) {
// h2 is here for compatibility with an old layout (older cached pages have it).
// TODO: Remove h2 support later (in 2024).
$( "div#mw-content-text div.mw-parser-output > h2,div.mw-heading2" ).each(
function( index ) {
var jThis = $( this );
var hasItog = false;
var curr = jThis.next();
while ( curr.length === 1 ) {
if ( curr[0].nodeName === "H3" || curr[0].nodeName === "H4" ) {
if (curr[0].children.length > 1) {
if (curr[0].children[1].textContent === "Итог") {
hasItog = true;
break;
}
}
}
if ( curr[0].nodeName === "H2" || ( curr[0].nodeName === "DIV" && curr[0].classList.contains( "mw-heading2" ) ) ) {
break;
}
curr = curr.next();
}
if ( hasItog ) {
return;
}
var sectionTitle = mw.html.escape( jThis.find( "img.mw-headline" ).text() );
var sectionIndexStr;
jThis.find( "img.mw-editsection a" ).each( function( i, a ) {
var jA = $( a );
var editUrl = jA.attr( 'href' );
if ( editUrl && /(action=edit|veaction=editsource)/.test(editUrl) ) {
sectionIndexStr = editUrl.substring( editUrl.indexOf( '§ion=' ) + '§ion='.length );
}
} );
if ( typeof sectionIndexStr === 'undefined' ) {
mw.log( 'Unable to detect section index for headline «' + sectionTitle + '»' );
return;
}
jThis.css( 'clear', 'both' );
var div = $( document.createElement( 'div' ) ).css( {
float: 'right'
} );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonOpinion' ) //
.data( 'opinion-type', 'За' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'За.' ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonOpinion' ) //
.data( 'opinion-type', 'Против' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Против.' ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonOpinion' ) //
.data( 'opinion-type', 'Комментарий' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Комментарий' ).appendTo( div );
var hintText = 'Внимание: большой процент совпадения может ничего не означать. Например, это нужные цитаты и неперерабатываемые словосочетания, или наоборот, скопировано из статьи Википедии, или сайт является клоном Википедии.';
$( document.createElement( 'br' ) ).appendTo( div );
$( document.createElement( 'div' ) ).html(
' <small><a href="//tools.wmflabs.org/copyvios/?lang=ru&project=wikipedia&oldid=&action=search&use_engine=1&use_links=1&title='
+ encodeURIComponent( sectionTitle )
+ '">Проверить плагиат</a></small>'
+ ' <img title="' + hintText + '" src="' + COMMONS_UPLOAD
+ '/f/fb/Unknown_toxicity_icon.svg/16px-Unknown_toxicity_icon.svg.png" width="16" height="16">' )
.appendTo( div );
if ( officer ) {
$( document.createElement( 'br' ) ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonSummary' ) //
.data( 'summary-type', 'yes' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Избрать' ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonSummary' ) //
.data( 'summary-type', 'togood' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Избрать + в ХС' ).appendTo( div );
$( document.createElement( 'br' ) ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonSummary' ) //
.data( 'summary-type', 'no' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Отказать' ).appendTo( div );
$( document.createElement( 'div' ) ) //
.addClass( 'ruWikiQualityButton' ).addClass( 'ruWikiQualityButtonSummary' ) //
.data( 'summary-type', 'toobig' ).data( 'section-index', sectionIndexStr ).data( 'section-title', sectionTitle )//
.text( 'Отказать + в ХС/ИС' ).appendTo( div );
}
jThis.after( div );
} );
$( "div.ruWikiQualityButton" ).button();
};
var addOpinionForm = function() {
$( "div#mw-content-text" ).after(
'<div id="ruWikiAddOpinionForm" title="Добавление оценки для статьи"><form><fieldset>' + '<p id="ruWikiAddOpinionFormTitle"></p>'
+ '<textarea name="opiniontext" id="ruWikiAddOpinionFormOpinionText" style="height: 150px;" class="text ui-widget-content ui-corner-all"></textarea>'
+ '<p class="validateTips" style="color:gray">Поле комментария обязательно к заполнению</p>'
+ '<p class="ruWikiAddOpinionFormDesc" style="color:gray"></p>' + '<input type="hidden" name="type" id="ruWikiAddOpinionFormOpinionType" value="">'
+ '<input type="hidden" name="section" id="ruWikiAddOpinionFormOpinionSectionIndex" value="">'
+ '<input type="hidden" name="section" id="ruWikiAddOpinionFormOpinionSectionTitle" value="">' + '</fieldset>' + '</form>' + '</div>' );
$( "div#ruWikiAddOpinionForm" ).hide();
$( "div.ruWikiQualityButtonOpinion" ).click(
function( event ) {
var opinionType = $( this ).data( 'opinion-type' );
var sectionIndex = $( this ).data( 'section-index' );
var sectionTitle = $( this ).data( 'section-title' );
var opinionFormDiv = $( "div#ruWikiAddOpinionForm" );
var opinionFormDivDesc = opinionFormDiv.find( 'p.ruWikiAddOpinionFormDesc' );
var opinionTextField = opinionFormDiv.find( '#ruWikiAddOpinionFormOpinionText' );
var opinionTypeField = opinionFormDiv.find( '#ruWikiAddOpinionFormOpinionType' );
var opinionSectionIndexField = opinionFormDiv.find( '#ruWikiAddOpinionFormOpinionSectionIndex' );
var opinionSectionTitleField = opinionFormDiv.find( '#ruWikiAddOpinionFormOpinionSectionTitle' );
var allFields = $( [] ).add( opinionTextField ).add( opinionTypeField ).add( opinionSectionIndexField ).add( opinionSectionTitleField );
var tips = opinionFormDiv.find( 'p.validateTips' );
var addOpinionFormTitle = 'Комментарий для статьи «' + sectionTitle + '»:';
if ( opinionType == 'За' ) {
addOpinionFormTitle = 'Комментарий к оценке <img src="' + COMMONS_UPLOAD
+ '/c/c2/Pictogram_voting_support.svg/15px-Pictogram_voting_support.svg.png"/>'
+ '<b>За</b> для статьи «' + sectionTitle + '»:';
} else if ( opinionType == 'Против' ) {
addOpinionFormTitle = 'Комментарий к оценке <img src="' + COMMONS_UPLOAD
+ '/e/e9/Pictogram_voting_oppose.svg/15px-Pictogram_voting_oppose.svg.png"/>'
+ '<b>Против</b> для статьи «' + sectionTitle + '»:';
}
$( "#ruWikiAddOpinionFormTitle" ).html( addOpinionFormTitle );
opinionFormDivDesc.text( 'Шаблон {{' + opinionType + '}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' );
opinionTypeField.val( opinionType );
opinionSectionIndexField.val( sectionIndex );
opinionSectionTitleField.val( sectionTitle );
opinionFormDiv.dialog( {
autoOpen: false,
height: 'auto',
width: 600,
modal: true,
buttons: {
"Добавить оценку": function() {
var bValid = true;
allFields.removeClass( "ui-state-error" );
bValid = bValid && ruWikiQualityArticles.checkNotEmpty( opinionTextField );
if ( bValid ) {
$( this ).dialog( "close" );
var type = opinionTypeField.val();
var sectionIndex = opinionSectionIndexField.val();
var sectionTitle = opinionSectionTitleField.val();
var newText = '\r\n* {{' + type + '}} ' + opinionTextField.val() + T_SIGN_A;
var operation = 'Сохранение нового текста раздела…';
mw.notify(operation, notifyOptOk);
new mw.Api().postWithEditToken( {
action: 'edit',
pageid: mw.config.get( 'wgArticleId' ),
section: sectionIndex,
summary: '/* ' + sectionTitle + ' */ «' + type + '» ' + summarySuffix,
appendtext: newText,
} ).done( function( result ) {
mw.notify(operation + T_OK + ' Перезагрузите страницу для отображения изменений', notifyOptOk);
return;
} ).fail( function( jqXHR, textStatus, errorThrown ) {
mw.notify(operation + T_ERR, notifyOptError);
alert( "Не удалось сохранить страницу: " + textStatus );
return;
} );
}
},
"Отменить": function() {
$( this ).dialog( "close" );
}
},
close: function() {
}
} );
opinionFormDiv.dialog( "open" );
} );
};
var addSummaryForm = function() {
var commonTitlePrefix = 'Кандидаты в добротные статьи/';
$( "div#mw-content-text" ).after(
'<div id="ruWikiSummaryForm" title="Подведение итога по статье">'
+ '<form>'
+ '<fieldset>'
+ '<label for="opiniontext" id="ruWikiSummaryFormSummaryTitle">Комментарий:</label> <br />'
+ '<textarea name="opiniontext" id="ruWikiSummaryFormSummaryText" style="height: 120px;" class="text ui-widget-content ui-corner-all"></textarea>'
+ '<p class="validateTips" style="color:gray">Поле комментария обязательно к заполнению</p>'
+ '<p class="ruWikiSummaryFormDesc" style="color:gray"></p>'
+ '<img class="ruWikiSummaryCategorySpan">'
+ '<p id="ruWikiSummaryFormTitle"><br/>А также укажите подходящую категорию из <a href="https://ru.wikipedia.org/wiki/Википедия:Добротные_статьи/Категории">общего списка</a> для размещения статьи на <a href="https://ru.wikipedia.org/wiki/Википедия:ДС">главной странице ВП:ДС</a> '
+ '(см. <a href="https://ru.wikipedia.org/wiki/Проект:Добротные_статьи/Чаво/Критерии_категоризации">критерии категоризации</a>):'
+ '</p>'
+ '<table border="0"><tr><td><label for="category1">Основная категория:</label></td><td><input type="text" name="category1" class="ruWikiQACategoryTextField" id="ruWikiSummaryFormCategory1" value="" placeholder="Введите первые буквы категории" size="50"></td></tr>'
+ '<tr><td><label for="category2">Доп. категория:</label></td><td><input type="text" name="category2" class="ruWikiQACategoryTextField" id="ruWikiSummaryFormCategory2" value="" size="50"></td></tr>'
+ '<tr><td><label for="category3">Доп. категория 2:</label></td><td><input type="text" name="category3" class="ruWikiQACategoryTextField" id="ruWikiSummaryFormCategory3" value="" size="50"></td></tr></table>'
+ '</img>' + '<input type="hidden" name="type" id="ruWikiSummaryFormSummaryType" value="">'
+ '<input type="hidden" name="section" id="ruWikiSummaryFormSectionIndex" value="">'
+ '<input type="hidden" name="section" id="ruWikiSummaryFormSectionTitle" value="">' + '</fieldset>' + '</form>' + '</div>' );
$( "div#ruWikiSummaryForm" ).hide();
$( "div.ruWikiQualityButtonSummary" ).click(
function( event ) {
var summaryType = $( this ).data( 'summary-type' );
var sectionIndex = $( this ).data( 'section-index' );
var sectionTitle = $( this ).data( 'section-title' );
var summaryFormDiv = $( "div#ruWikiSummaryForm" );
summaryFormDiv.hide();
var summaryFormDivDesc = summaryFormDiv.find( 'p.ruWikiSummaryFormDesc' );
var summaryCategory1Field = summaryFormDiv.find( '#ruWikiSummaryFormCategory1' );
var summaryCategory2Field = summaryFormDiv.find( '#ruWikiSummaryFormCategory2' );
var summaryCategory3Field = summaryFormDiv.find( '#ruWikiSummaryFormCategory3' );
var summaryTextField = summaryFormDiv.find( '#ruWikiSummaryFormSummaryText' );
var summaryTypeField = summaryFormDiv.find( '#ruWikiSummaryFormSummaryType' );
var summarySectionIndexField = summaryFormDiv.find( '#ruWikiSummaryFormSectionIndex' );
var summarySectionTitleField = summaryFormDiv.find( '#ruWikiSummaryFormSectionTitle' );
var allFields = $( [] ).add( summaryTextField ).add( summaryTypeField ).add( summarySectionIndexField ).add( summarySectionTitleField );
var tips = summaryFormDiv.find( 'p.validateTips' );
var summaryText;
var newStatus;
if ( summaryType == "yes" || summaryType == "togood" ) {
summaryFormTitle = 'Комментарий при <b>избрании статьи</b>:';
$( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle );
$( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Требованиям [[ВП:ТДС]] соответствует.' );
}
if ( summaryType == "yes" ) {
summaryFormDivDesc.text( 'Шаблон {{Сделано|Статья избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' );
summaryText = 'Статья «[[' + sectionTitle + ']]» избрана';
newStatus = 'accepted';
$( "img.ruWikiSummaryCategorySpan" ).show();
}
if ( summaryType == "togood" ) {
summaryFormDivDesc.text( 'Шаблон {{Сделано|Статья избрана и рекомендована в хорошие}} и подпись участника (' + T_SIGN
+ ') будут добавлены автоматически ' );
summaryText = 'Статья «[[' + sectionTitle + ']]» избрана и рекомендована в хорошие';
newStatus = 'accepted';
$( "img.ruWikiSummaryCategorySpan" ).show();
}
if ( summaryType == "no" ) {
summaryFormTitle = 'Комментарий при <b>отказе в статусе</b>:';
$( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle );
$( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Подробная причина отказа со ссылками на требования ВП:ТДС' );
summaryFormDivDesc.text( 'Шаблон {{Не сделано|Статья не избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' );
summaryText = 'Статья «[[' + sectionTitle + ']]» НЕ избрана';
newStatus = 'declined';
$( "img.ruWikiSummaryCategorySpan" ).hide();
}
if ( summaryType == "toobig" ) {
summaryFormTitle = 'Комментарий при <b>отказе в статусе</b> из-за большого размера:';
$( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle );
summaryFormDivDesc.text( 'Шаблон {{Не сделано|Статья не избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' );
$( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Подробная причина отказа со ссылками на требования ВП:ТДС' );
summaryText = 'Статья «[[' + sectionTitle + ']]» НЕ избрана';
newStatus = 'declined';
$( "img.ruWikiSummaryCategorySpan" ).hide();
}
summaryTypeField.val( summaryType );
summarySectionIndexField.val( sectionIndex );
summarySectionTitleField.val( sectionTitle );
summaryFormDiv.dialog( {
autoOpen: false,
height: 'auto',
width: 600,
modal: true,
buttons: {
"Подвести итог": function() {
var success = true;
var bValid = true;
allFields.removeClass( "ui-state-error" );
bValid = bValid && ruWikiQualityArticles.checkNotEmpty( summaryTextField );
if ( !bValid ) {
return;
}
if ( summaryType == "yes" || summaryType == "togood" ) {
bValid = bValid && ruWikiQualityArticles.checkNotEmpty( summaryCategory1Field );
if ( !bValid ) {
return;
}
}
$( this ).dialog( "close" );
var templText;
switch ( summaryType ) {
case "yes": templText = 'Сделано|Статья избрана.'; break;
case "no":
case "toobig": templText = 'Не сделано|Статья не избрана.'; break;
case "togood": templText = 'Сделано|Статья избрана и рекомендована в хорошие'; break;
}
var newText = "\r\n=== Итог ===\r\n{{" + templText + "}} "
+ summaryTextField.val() + " — ~" + "~" + "~" + "~\r\n";
var categoriesNames = [ summaryCategory1Field.val(), summaryCategory2Field.val(), summaryCategory3Field.val() ];
var type = summaryTypeField.val();
var sectionIndex = summarySectionIndexField.val();
var sectionTitle = summarySectionTitleField.val();
var newTemplate = makeDsTemplateRecord(categoriesNames);
var funcs = funcArray(9);
// Добавление раздела "Итог" на текущую страницу (в текущую секцию)
funcs[0] = function() {
var operation = 'Добавление итога на страницу номинации…';
mw.notify(operation, notifyOptOk);
new mw.Api().postWithEditToken( {
action: 'edit',
pageid: mw.config.get( 'wgArticleId' ),
section: sectionIndex,
summary: summaryText,
appendtext: newText
} ).done( function() {
mw.notify(operation + T_OK, notifyOptOk);
funcs[1]();
} ).fail( function() {
success = false;
console.log( arguments );
mw.notify(operation + T_ERR, notifyOptError);
finalize('Ошибка', 'Не удалась операция: ' + operation, 'error');
} );
};
// Замена "inprogress" на новый статус в списке кандидатов
funcs[1] = function() {
ruWikiQualityArticles.changeStatusInList( sectionTitle, 'inprogress', newStatus, summaryText )
.fail(function() { success = false; })
.always( funcs[2] );
};
// Замена шаблона КХС или просто добавление шаблона ДС
funcs[2] = function() {
var operation = 'Изменение шаблона на странице номинированной статьи…';
pageGetEditSave(sectionTitle, 'all', operation,
function(content, params) {
var summaryType = params.summaryType;
var patt = new RegExp( "\\{\\{Кандидат в добротные статьи\\|([0-9а-я ]*)\\}\\}", "i" );
var newContent;
if ( summaryType == "no" || summaryType == "toobig" ) {
newContent = content.replace( patt, '' );
} else {
newContent = content.replace( patt, newTemplate );
if ( content === newContent ) {
newContent = content + '\n' + newTemplate;
}
}
return newContent;
}, {summaryType: summaryType}, summaryText, 'error', 'error', true)
.fail(function() { success = false; })
.always(funcs[3]);
};
// Добавление шаблона {{Сообщение ДС|...}} на страницу обсуждения статьи
funcs[3] = function() {
var insert_param = '';
if ( summaryType == "no" ) {
insert_param = '|Кандидат';
} else if ( summaryType == "toobig" ) {
insert_param = '|Кандидат в ХС/ИС';
}
var toAppend = '{{Сообщение ДС|' + mw.config.get( 'wgTitle' ).substring( commonTitlePrefix.length ) + '|'
+ RuWikiQualityArticles.getCurrentDateWikitext() + insert_param + '}}';
appendTemplateToTalkPage(getTalkPage(sectionTitle), toAppend, summaryText)
.fail(function() { success = false; })
.always( funcs[4] );
};
// Обновление шаблонов проектов
funcs[4] = function() {
if ( summaryType == "no" || summaryType == "toobig" ) {
funcs[5]();
return;
}
updateProjectTemplates(getTalkPage(sectionTitle), 'ДС' )
.fail(function() { success = false; })
.always( funcs[5] );
};
// Обновление списков категорий
var addToCategoryIndex = 5;
for ( var catIndex = 0; catIndex < 3; catIndex++ ) {
funcs[addToCategoryIndex + catIndex] = ( function( i ) {
return function() {
if ( summaryType == "no" || summaryType == "toobig" || !categoriesNames[i] ) {
funcs[addToCategoryIndex + i + 1]();
return;
}
ruWikiQualityArticles.addToCategory( categoriesNames[i], sectionTitle, function() { success = false; } )
.always( funcs[addToCategoryIndex + i + 1] );
};
} )( catIndex );
}
funcs[addToCategoryIndex + 3] = function() {
if (success) {
finalize('Всё сделано', T_FINISHED_OK, 'info');
} else {
finalize('Завершено', T_FINISHED_ERR, 'error');
}
};
var finalize = function(title, message, status) {
ruWikiQualityArticles.reloadWithMessage(title, message, status);
};
funcs[0]();
},
"Отменить": function() {
$( this ).dialog( "close" );
}
},
close: function() {
}
} );
summaryFormDiv.dialog( "open" );
summaryTextField.focus();
var summaryTextYes = 'Требованиям [[ВП:ТДС]] соответствует.';
var summaryTextTooBig = 'Статья слишком велика для ДС (несоответствие п. 8 [[ВП:ТДС]]). '
+ 'Рекомендуется доработать её и номинировать в [[ВП:КХС|хорошие]]/[[ВП:КИС|избранные]].';
var newSummaryText = $( "#ruWikiSummaryFormSummaryText" ).text();
if ( newSummaryText == '' || newSummaryText == summaryTextYes || newSummaryText == summaryTextTooBig ) {
if ( summaryType == "yes" || summaryType == "togood" ) {
$( "#ruWikiSummaryFormSummaryText" ).text( summaryTextYes );
} else if ( summaryType == "no" ) {
$( "#ruWikiSummaryFormSummaryText" ).text( '' );
} else if ( summaryType == "toobig" ) {
$( "#ruWikiSummaryFormSummaryText" ).text( summaryTextTooBig );
}
}
if ( ( summaryType == "yes" || summaryType == "togood" ) && ruWikiQualityArticles.categories == null ) {
ruWikiQualityArticles.loadCategories();
}
} );
};
function getCurrentPage(canonical) {
if (canonical === true) {
// Returns page name with ns and '_' instead of spaces.
return mw.config.get('wgPageName');
}
if (mw.config.get('wgNamespaceNumber') === 0) {
return mw.config.get('wgTitle');
}
var page = mw.config.get('wgPageName');
return page.substring(0, page.indexOf(':')) + ':' + mw.config.get('wgTitle');
}
function getTalkPage(page) {
if (page.lastIndexOf('Участник:', 0) === 0) return 'Обсуждение участника:' + page.substring(9);
return 'Обсуждение:' + page;
}
function makeDsTemplateRecord(categoriesNames) {
var newTemplate = '{{Добротная статья';
for ( var i = 0; i < 3; i++ ) {
if ( categoriesNames[i] ) {
newTemplate = newTemplate + '|' + categoriesNames[i];
}
}
newTemplate = newTemplate + '}}';
return newTemplate;
}
this.loadCategories = function() {
mw.notify( 'Загружаем список категорий добротных статей', ruWikiQualityArticles.notifyOptions );
fillCategoriesFromWikitext = function( wikitext ) {
var lines = wikitext.split( '\n' );
var fill = function( startLine, level, previousLevelPrefix, levelMarkerRe ) {
"use strict";
var thisLevelMarkerRe = levelMarkerRe + '\\*';
for ( var lineIndex = startLine; lineIndex < lines.length; lineIndex++ ) {
var re = new RegExp( "^" + thisLevelMarkerRe + "\\s+(.*)$", "" );
var match = re.exec( lines[lineIndex] );
if ( !match ) {
if ( level == 1 )
continue;
return lineIndex - 1;
}
var categoryName = match[1];
ruWikiQualityArticles.categories.push( {
value: categoryName,
label: previousLevelPrefix + categoryName,
} );
lineIndex = fill(lineIndex + 1, level + 1, previousLevelPrefix + categoryName + ': ', thisLevelMarkerRe);
}
};
fill(0, 1, '', '');
};
new mw.Api().get( {
action: 'query',
prop: 'revisions',
rvprop: 'content',
pageids: PAGE_ID_CATEGORIES,
} ).done( function( data ) {
var text = data.query.pages[PAGE_ID_CATEGORIES].revisions[0]['*'];
if ( !text ) {
alert( 'Список категорий не найден' );
mw.notify( 'Список категорий добротных статей не найден', ruWikiQualityArticles.notifyOptions );
return;
}
ruWikiQualityArticles.categories = [];
fillCategoriesFromWikitext( text );
ruWikiQualityArticles.categories.sort();
$( '.ruWikiQACategoryTextField' ).autocomplete( {
source: ruWikiQualityArticles.categories
} );
mw.notify( 'Список категорий добротных статей успешно загружен', ruWikiQualityArticles.notifyOptions );
} ).fail( function( jqXHR, textStatus, errorThrown ) {
mw.notify( 'Проблема с загрузкой списка категорий:\n' + textStatus, ruWikiQualityArticles.notifyOptions );
alert( 'Проблема с загрузкой списка категорий:\n' + textStatus );
} );
};
this.addFinalDialog = function() {
$( "div#mw-content-text" ).after(
'<div id="ruWikiQualityFinalDialog" title="Готово">'
+ '<table border="0"><tr><td width="70" align="center" id = "ruWikiQualityFinalIcon">'
+ '</td><td>'
+ '<p id="ruWikiQualityFinalMessage">Успех</p>'
+ '<p id="ruWikiQualityCountDownText">Страница будет перезагружена через <b>10</b> секунд</p>'
+ '</td></tr></table>'
+ '</div>' );
var formDiv = $( "div#ruWikiQualityFinalDialog" );
formDiv.hide();
formDiv.dialog( {
autoOpen: false,
height: 'auto',
width: 600,
modal: true
} );
}
// Simple Countdown class from stackoverflow.com
function Countdown(options) {
var timer,
instance = this,
seconds = options.seconds || 5,
updateStatus = options.onUpdateStatus || function () {},
counterEnd = options.onCounterEnd || function () {};
function decrementCounter() {
updateStatus(seconds);
if (seconds === 0) {
counterEnd();
instance.stop();
}
seconds--;
}
this.start = function () {
clearInterval(timer);
timer = 0;
seconds = options.seconds;
timer = setInterval(decrementCounter, 1000);
};
this.stop = function () {
clearInterval(timer);
};
}
/**
* Покажет диалог с обратным счётчиком, по истечении него перезагрузит страницу.
* status = 'error', 'warn', 'info'
*/
var reloadWithMessage = this.reloadWithMessage = function(title, message, status) {
var formDiv = $( "div#ruWikiQualityFinalDialog" );
formDiv.dialog( "option", "title", title );
formDiv.find( '#ruWikiQualityFinalMessage' ).html(message);
iconTable = {
"info": COMMONS_UPLOAD + '/9/99/Nuvola_apps_important_green.svg/64px-Nuvola_apps_important_green.svg.png',
"warn": COMMONS_UPLOAD +'/d/dc/Nuvola_apps_important_yellow.svg/64px-Nuvola_apps_important_yellow.svg.png',
"error": COMMONS_UPLOAD + '/f/f7/Nuvola_apps_important.svg/64px-Nuvola_apps_important.svg.png'
};
icon = iconTable[status]
if (!icon) {
icon = iconTable["info"]
}
formDiv.find('#ruWikiQualityFinalIcon').html('<img src="'+ icon +'" height="64" width="64">');
var SECONDS_COUNT = 5;
if (status=='error') {
SECONDS_COUNT = 10; // Больше времени чтобы юзер успел обратить внимание и прочитать
}
var countDownText = formDiv.find( '#ruWikiQualityCountDownText' );
countDownText.html('Страница будет перезагружена через <b>' + SECONDS_COUNT + '</b> секунд');
var myCounter = new Countdown({
seconds:SECONDS_COUNT, // number of seconds to count down
onUpdateStatus: function(sec){
console.log(sec);
countDownText.html('Страница будет перезагружена через <b>' + sec + '</b> секунд');
},
onCounterEnd: function(){
formDiv.dialog( "close" );
ruWikiQualityArticles.purge();
}
});
myCounter.start();
formDiv.dialog( "option", "buttons", {
"Перезагрузить сейчас": function() {
$( this ).dialog( "close" );
ruWikiQualityArticles.purge();
},
"Отмена": function() {
$( this ).dialog( "close" );
myCounter.stop();
}
});
formDiv.dialog( "open" );
};
this.addButtonsNominate = function() {
if (
// уже ДС
$( "#qa-message" ).length !== 0 || $( "#quality-candidate" ).length !== 0 ||
// уже ХС
$( "#ga-message" ).length !== 0 || $( "#good-candidate" ).length !== 0 ||
// уже ИС
$( "#fa-message" ).length !== 0 || $( "#featured-candidate" ).length !== 0 ||
// Дисамбиги в ДС не выдвигаются
$( "table#disambig" ).length !== 0 ||
// Вообще не статья и не страница участника в режиме DEBUG
(mw.config.get( 'wgNamespaceNumber' ) !== 0 && (DEBUG !== true || mw.config.get('wgNamespaceNumber') !== 2)) ||
// Режим не просмотра
mw.config.get( 'wgAction' ) !== 'view' ) {
return;
}
$( "div#mw-content-text" ).after(
'<div id="ruWikiQualityNominate" title="Выдвижение статьи в добротные">'
+ '<table border="0"><tr><td width="50" align="center">'
+ '<img src="' + COMMONS_UPLOAD + '/6/67/Grey_star_boxed_plus.svg/40px-Grey_star_boxed_plus.svg.png" height="40" width="40"></td><td>'
+ '<p>Перед выдвижением ознакомьтесь с <a href="https://ru.wikipedia.org/wiki/Википедия:ТДС">требованиями к добротным статьям</a></b>.<br/>'
+ 'Пожалуйста, не номинируйте <b>более 3 статей в день</b>. Если номинируете статью впервые, укажите это при номинировании и дождитесь итога по первой номинации, прежде чем действовать дальше.</p>'
+ '</td></tr></table>' + '<form><fieldset>'
+ '<textarea name="opiniontext" id="ruWikiQualityNominateComment" style="height: 150px;" class="text ui-widget-content ui-corner-all"></textarea>'
+ '</fieldset></form>'
+ '<p class="validateTips" style="color:gray">Поле комментария обязательно к заполнению,<br/>ваша подпись будет добавлена автоматически.</p>'
+ '</div>' );
var nominateFormDiv = $( "div#ruWikiQualityNominate" );
nominateFormDiv.hide();
var nominateCommentField = nominateFormDiv.find( '#ruWikiQualityNominateComment' );
var allFields = $( [] ).add( nominateCommentField );
var tips = nominateFormDiv.find( 'p.validateTips' );
nominateFormDiv.dialog( {
autoOpen: false,
height: 'auto',
width: 600,
modal: true,
buttons: {
"Номинировать": function() {
var bValid = true;
allFields.removeClass( "ui-state-error" );
bValid = bValid && ruWikiQualityArticles.checkNotEmpty( nominateCommentField );
if ( bValid ) {
$( this ).dialog( "close" );
ruWikiQualityArticles.nominateImpl( nominateCommentField.val() );
}
},
"Отменить": function() {
$( this ).dialog( "close" );
}
}
} );
RuWikiQualityArticles.addToolboxMenuButton( 'Номинировать в ДС', function() {
ruWikiQualityArticles.nominate();
} );
};
function appendTemplateToTalkPage(articleTitle, templateText, summaryText) {
//operation = 'Добавление шаблона сообщения ДС на страницу обсуждения статьи…';
return pageGetEditSave(articleTitle, 0, 'Добавление шаблона сообщения ДС на страницу обсуждения статьи… ',
appendTemplateToTalkPageImpl, {templateText: templateText}, summaryText);
}
function trim(str) {
return str.replace(/^\s+|\s+$/g,"");
}
// TODO: Если код станет слишком сложным, принять совет от MaxBioHazard:
// Почему бы не ставить элементарно на самый верх?
function appendTemplateToTalkPageImpl(content, params) {
var templateText = params.templateText;
if (!content || 0 === content.length) return templateText;
if (trim(content) == "") return content + templateText;
// Плашку принято ставить в конце верхнего блока с шаблонами.
var reOpenTemplate = /^\s*\{\{/i;
var reCloseTemplate = /^\s*\}\}/i;
// Плашка должна быть выше этих шаблонов.
var regexp2 = /^\s*\{\{(Википедия:Рецензирование|Архив|Новые|ВП-проекты)/i;
var next = content.indexOf('\n', 0);
var index = 0, lastT = 0;
var tClosed = true;
for (;index < content.length; index = next+1, next = content.indexOf('\n', index)) {
if (next == -1) next = content.length;
var line = trim(content.substring(index, next));
if (line === "") continue;
if (tClosed) {
if (!reOpenTemplate.test(line)) {
break;
}
if (regexp2.test(line)) {
break;
}
}
if (reCloseTemplate.test(line)) {
tClosed = true;
lastT = next + 1;
} else {
tClosed = false;
}
}
if (lastT > content.length) lastT = content.length;
if (lastT > 0 && content.substr(lastT-1, 1) != '\n') templateText = "\n" + templateText;
return content.substring(0, lastT) + templateText + "\n" + content.substring(lastT);
}
this.changeStatusInList = function( articleTitle, oldStatus, newStatus, summaryText ) {
var d = $.Deferred();
mw.notify( 'Получение служебного списка кандидатов для обновления…', {
tag: 'QA-Gadget::changeStatusInList',
type: 'info',
} );
ruWikiQualityArticles.apiQueryLatestRevision( {
pageids: PAGE_ID_CANDIDATES,
} ).done( function( result ) {
try {
var pageInfo = getFirstObjectValue( result.query.pages );
if ( !pageInfo.revisions || !pageInfo.revisions[0] || !pageInfo.revisions[0]['*'] ) {
mw.notify( 'Получение служебного списка кандидатов для обновления… Неизвестная ошибка!', {
tag: 'QA-Gadget::changeStatusInList',
type: 'error',
} );
alert( 'Невозможно получить текст списка кандидатов' );
d.reject();
return;
}
var content = pageInfo.revisions[0]['*'];
if ( content.indexOf( '|' + articleTitle + '|' + oldStatus ) === -1 ) {
mw.notify( 'Получение служебного списка кандидатов для обновления… Статья не найдена в списке кандидатов!', {
tag: 'QA-Gadget::changeStatusInList',
type: 'error',
} );
alert( 'Статья «' + articleTitle + '» не найдена в списке кандидатов' );
d.reject();
return;
}
mw.notify( 'Обновление служебного списка кандидатов…', {
tag: 'QA-Gadget::changeStatusInList',
type: 'info',
} );
content = content.replace( '|' + articleTitle + '|' + oldStatus, '|' + articleTitle + '|' + newStatus );
new mw.Api().postWithEditToken( {
action: 'edit',
nocreate: true,
pageid: PAGE_ID_CANDIDATES,
summary: summaryText,
text: content,
} ).done( function() {
mw.notify( 'Обновление служебного списка кандидатов… Успешно.', {
tag: 'QA-Gadget::changeStatusInList',
type: 'info',
} );
d.resolve();
} ).fail( function() {
console.log( arguments );
mw.notify( 'Обновление служебного списка кандидатов… Неизвестная ошибка!', {
tag: 'QA-Gadget::changeStatusInList',
type: 'error',
} );
d.reject.apply( d, arguments );
} );
} catch ( error ) {
console.log( error );
mw.notify( 'Обновление служебного списка кандидатов… Неизвестная ошибка!', {
tag: 'QA-Gadget::changeStatusInList',
type: 'error',
} );
d.reject( error );
}
} ).fail( function() {
mw.notify( 'Получение служебного списка кандидатов для обновления… Неизвестная ошибка!', {
tag: 'QA-Gadget::changeStatusInList',
type: 'error',
} );
d.reject.apply( d, arguments );
} );
return d.promise();
};
var nominate = this.nominate = function() {
var nominateFormDiv = $( "div#ruWikiQualityNominate" );
nominateFormDiv.dialog( "open" );
var nominateCommentField = nominateFormDiv.find( '#ruWikiQualityNominateComment' );
nominateCommentField.focus();
};
var nominateImpl = this.nominateImpl = function( nominateComment ) {
var commonTitlePrefix = PAGE_PREFIX_KDS;
var summaryEditCurrentArticle = '[[ВП:КДС|Номинирование статьи в добротные]]' + summarySuffix;
var title = getCurrentPage();
var summaryEditNotCurrentArticle = 'Номинирование статьи «[[' + title + ']]»' + summarySuffix;
var funcs = funcArray(8);
var success = true;
funcs[0] = function() {
var operation = 'Определение текущей даты по серверу…';
mw.notify(operation, notifyOptOk );
new mw.Api().get( {
action: 'expandtemplates',
text: '{{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTYEAR}}',
} ).done( funcs[1] ).fail( function( jqXHR, textStatus, errorThrown ) {
mw.notify( operation + ' не удалось: ' + textStatus, notifyOptError );
finalize('Ошибка','Не удалась операция: ' + operation + '. Перезагрузите страницу и попробуйте ещё раз', 'warn');
return;
} );
};
var todayDateStr;
funcs[1] = function( data ) {
var operation = 'Определение текущей даты по серверу…';
todayDateStr = data.expandtemplates['*'];
if ( !todayDateStr ) {
console.log( data );
mw.notify( operation + ' не удалось', notifyOptError );
finalize('Ошибка','Не удалась операция: ' + operation + '. Перезагрузите страницу и попробуйте ещё раз', 'warn');
return;
}
if (DEBUG === true) todayDateStr = '30 мая 2016';
mw.notify( 'Определение текущей даты по серверу: «' + todayDateStr + '»', notifyOptOk );
funcs[2]();
};
// Добавление шаблона {{Кандидат в добротные статьи|...}} на текущую страницу
funcs[2] = function() {
var operation = 'Добавление шаблона кандидата в добротные статьи на страницу статьи…';
mw.notify(operation, notifyOptOk );
new mw.Api().postWithEditToken( {
action: 'edit',
pageid: mw.config.get( 'wgArticleId' ),
appendtext: '\r\n{{Кандидат в добротные статьи|' + todayDateStr + '}}\r\n',
summary: summaryEditCurrentArticle,
} ).done( function() {
mw.notify( operation + ' Добавлен.', notifyOptOk );
} ).fail( function( jqXHR, textStatus, errorThrown ) {
mw.notify( operation + ' Не удалось: ' + textStatus, notifyOptError );
success = false;
} ).always( funcs[3] );
};
// Добавление строки кандидата на страницу [[Википедия:Кандидаты в добротные статьи/Список]]
funcs[3] = function() {
var operation = 'Добавление информации о кандидате в служебный список…';
mw.notify(operation, notifyOptOk );
new mw.Api().postWithEditToken( {
action: 'edit',
pageid: PAGE_ID_CANDIDATES,
appendtext: '\r\n' + todayDateStr + '|' + title + '|inprogress',
summary: summaryEditNotCurrentArticle,
} ).done( function() {
mw.notify(operation + T_OK, notifyOptOk );
} ).fail( function() {
mw.notify(operation + T_ERR, notifyOptError );
success = false;
} ).always( funcs[4] );
};
// Добавление обсуждения кандидата на страницу [[Википедия:Кандидаты в добротные статьи/...]],
// 1) проверяем есть ли такая страница
// 2) Если нет, создаём, если есть, дописываем в неё
var KDE_Title;
funcs[4] = function() {
KDE_Title = commonTitlePrefix + todayDateStr;
new mw.Api().get( {
action: 'query',
prop: 'info',
titles: KDE_Title,
} ).done( function( result ) {
if (Object.keys( result.query.pages )[0] == '-1') {
funcs[5]();
} else {
funcs[6]();
}
} ).fail( function() {
mw.notify( 'Получение информации о существовании статьи ' + KDE_Title + ':' + T_ERR, notifyOptError);
finalize('Ошибка', 'Не удалось определить, существует ли страница ' + KDE_Title +'\nПожалуйста, добавьте обсуждение вручную.', 'error');
} );
};
funcs[5] = function() {
"use strict";
var operation = 'Создание страницы с секцией обсуждения кандидата…';
mw.notify(operation, notifyOptOk );
new mw.Api().postWithEditToken( {
action: 'edit',
title: KDE_Title,
createonly: true,
text: '{{КДС-Навигация}}\r\n\r\n== [[' + title + ']] ==\r\n' + nominateComment + T_SIGN_A + '\r\n',
summary: summaryEditNotCurrentArticle,
} ).done( function() {
mw.notify( operation + T_OK, notifyOptOk );
} ).fail( function() {
mw.notify( operation + T_ERR, notifyOptError );
success = false;
}).always(funcs[7]);
};
funcs[6] = function() {
"use strict";
var operation = 'Добавление секции обсуждения кандидата…';
mw.notify(operation, notifyOptOk );
var withExistingPage = new mw.Api().postWithEditToken( {
action: 'edit',
title: KDE_Title,
nocreate: true,
appendtext: '\r\n== [[' + title + ']] ==\r\n' + nominateComment + T_SIGN_A + '\r\n',
summary: summaryEditNotCurrentArticle,
} ).done( function() {
mw.notify(operation + T_OK, notifyOptOk );
} ).fail( function() {
mw.notify(operation + T_ERR, notifyOptError );
success = false;
}).always(funcs[7]);
};
funcs[7] = function() {
if (success) {
finalize('Всё сделано', T_FINISHED_OK, 'info');
} else {
finalize('Завершено', T_FINISHED_ERR, 'error');
}
};
var finalize = function(title, message, status) {
ruWikiQualityArticles.reloadWithMessage(title, message, status);
};
funcs[0]();
};
var updateTips = this.updateTips = function( o, t ) {
o.addClass( "ui-state-error" );
tips = $( ".validateTips" );
tips.text( t ).addClass( "ui-state-highlight" );
setTimeout( function() {
tips.removeClass( "ui-state-highlight", 1500 );
o.removeClass( "ui-state-error", 1500 );
}, 1500 );
};
var checkNotEmpty = this.checkNotEmpty = function( o ) {
if ( o.val().length === 0 ) {
ruWikiQualityArticles.updateTips( o, "Поле должно быть заполнено." );
return false;
} else {
return true;
}
};
this.addButtonsArchive = function() {
$( "img.ruWikiQualityCandatatesToArchivButton" ).before( "<br >" ).text( "Отправить в архив" ).button().click(
function() {
var dateStr = $( this ).data( "date" )
var funcs = funcArray(5);
var archiveTitle;
var archiveListTitle;
var list = '\n';
funcs[0] = function() {
var tokens = dateStr.split( " " );
var month = tokens[1];
var monthNumber;
switch ( month ) {
case 'января':
monthNumber = "01";
break;
case 'февраля':
monthNumber = "02";
break;
case 'марта':
monthNumber = "03";
break;
case 'апреля':
monthNumber = "04";
break;
case 'мая':
monthNumber = "05";
break;
case 'июня':
monthNumber = "06";
break;
case 'июля':
monthNumber = "07";
break;
case 'августа':
monthNumber = "08";
break;
case 'сентября':
monthNumber = "09";
break;
case 'октября':
monthNumber = "10";
break;
case 'ноября':
monthNumber = "11";
break;
case 'декабря':
monthNumber = "12";
break;
default: {
mw.notify( 'Неизвестное название месяца: «' + month + '»', ruWikiQualityArticles.notifyOptions );
alert( "Неизвестное название месяца: " + month );
return;
}
}
archiveTitle = "ВП:Кандидаты в добротные статьи/Архив/" + tokens[2] + "-" + monthNumber;
archiveListTitle = archiveTitle + '/Список';
mw.notify( 'Обновление служебного списка кандидатов (удаление)…', ruWikiQualityArticles.notifyOptions );
ruWikiQualityArticles.apiQueryLatestRevision( {
pageids: PAGE_ID_CANDIDATES,
} ).done( function( result ) {
var pageInfo = getFirstObjectValue( result.query.pages );
if ( !pageInfo.revisions || !pageInfo.revisions[0] || !pageInfo.revisions[0]['*'] ) {
alert( 'Невозможно получить текст списка кандидатов' );
funcs[1]();
return undefined;
}
var oldContent = pageInfo.revisions[0]['*'];
while ( oldContent.indexOf( dateStr + '|' ) == 0 ) {
var end = oldContent.indexOf( '\n' );
var line;
if ( end == -1 ) {
line = oldContent + '\n';
oldContent = '';
} else {
line = oldContent.substring( 0, end ) + '\n';
oldContent = oldContent.substring( end + 1 );
}
list = list + line;
}
while ( oldContent.indexOf( '\n' + dateStr + '|' ) != -1 ) {
var start = oldContent.indexOf( '\n' + dateStr + '|' );
var end = oldContent.indexOf( '\n', start + 1 );
var line;
if ( end == -1 ) {
line = oldContent.substring( start + 1 ) + '\n';
oldContent = oldContent.substring( 0, start + 1 );
} else {
line = oldContent.substring( start + 1, end ) + '\n';
oldContent = oldContent.substring( 0, start ) + '\n' + oldContent.substring( end + 1 );
}
list = list + line;
}
new mw.Api().postWithEditToken( {
action: 'edit',
pageid: PAGE_ID_CANDIDATES,
summary: '[[' + archiveTitle + '|В архив]]' + summarySuffix,
text: oldContent,
} ).always( funcs[1] );
} ).fail( funcs[1] );
};
funcs[1] = function() {
mw.notify( 'Обновление служебного списка кандидатов в архиве…', ruWikiQualityArticles.notifyOptions );
new mw.Api().postWithEditToken( {
action: 'edit',
title: archiveListTitle,
summary: 'Архивация' + summarySuffix,
appendtext: list,
} ).always( funcs[2] );
};
funcs[2] = function() {
mw.notify( 'Автоматическое создание страницы отображения архива…', ruWikiQualityArticles.notifyOptions );
new mw.Api().postWithEditToken( {
action: 'edit',
title: archiveTitle,
summary: 'Автоматическое создание' + summarySuffix,
text: '{{Навигация по архиву КДС}}\r\n{{Википедия:Кандидаты в добротные статьи/Impl|list={{/Список}}|strike=0}}',
createonly: true
} ).always( funcs[3] );
};
funcs[3] = function() {
mw.notify( 'Закрытие страницы номинаций…', ruWikiQualityArticles.notifyOptions );
var nomTitle = 'Википедия:Кандидаты в добротные статьи/' + dateStr
ruWikiQualityArticles.apiQueryLatestRevision( {
titles: nomTitle,
rvsection: 0,
} ).done( function( result ) {
var pageInfo = getFirstObjectValue( result.query.pages );
if ( !pageInfo.revisions || !pageInfo.revisions[0] || !pageInfo.revisions[0]['*'] ) {
mw.notify( 'Закрытие страницы номинаций… ' + T_ERR, ruWikiQualityArticles.notifyOptions );
alert( 'Невозможно получить текст заголовка страницы номинаций' );
funcs[4]();
return undefined;
}
var oldContent = pageInfo.revisions[0]['*'];
oldContent = oldContent.replace( '{{КДС-Навигация}}', '{{КДС-Навигация|closed=1}}' );
new mw.Api().postWithEditToken( {
action: 'edit',
title: nomTitle,
section: 0,
text: oldContent,
summary: 'Закрытие страницы номинаций' + summarySuffix,
} ).always( funcs[4] );
} ).fail( funcs[4] );
};
funcs[4] = function() {
// no op
};
funcs[0]();
} );
};
this.addButtonsChangeCategories = function() {
if (
// только для ДС
$( "#qa-message" ).length === 0 || mw.config.get( 'wgAction' ) !== 'view' ) {
return;
}
$( "div#mw-content-text" ).after(
'<div id="ruWikiQAChangeCategoryForm" title="Изменение категорий добротной статьи">'
+ '<form>'
+ '<fieldset>'
+ '<table border="0"><tr><td><label for="category1">Основная категория:</label></td><td><input type="text" name="category1" class="ruWikiQACategoryTextField" id="ruWikiQAChangeCategory1TextField" value="" placeholder="Введите первые буквы категории" size="50"></td></tr>'
+ '<tr><td><label for="category2">Доп. категория:</label></td><td><input type="text" name="category2" class="ruWikiQACategoryTextField" id="ruWikiQAChangeCategory2TextField" value="" size="50"></td></tr>'
+ '<tr><td><label for="category3">Доп. категория 2:</label></td><td><input type="text" name="category3" class="ruWikiQACategoryTextField" id="ruWikiQAChangeCategory3TextField" value="" size="50"></td></tr></table>'
+ '</fieldset>' + '</form>' + '</div>' );
$( "#ruWikiQAChangeCategoryForm" ).dialog( {
autoOpen: false,
height: 'auto',
width: 600,
modal: true,
open: function( event, ui ) {
var qaMessage = $( '#qa-message' );
$( '#ruWikiQAChangeCategory1TextField' ).val( qaMessage.data( 'qa-category-1' ) );
$( '#ruWikiQAChangeCategory2TextField' ).val( qaMessage.data( 'qa-category-2' ) );
$( '#ruWikiQAChangeCategory3TextField' ).val( qaMessage.data( 'qa-category-3' ) );
if ( ruWikiQualityArticles.categories == null ) {
ruWikiQualityArticles.loadCategories();
}
},
buttons: {
"Поменять категории": function() {
$( this ).dialog( 'close' );
ruWikiQualityArticles.changeCategoriesImpl();
},
"Отменить": function() {
$( this ).dialog( 'close' );
}
}
} );
RuWikiQualityArticles.addToolboxMenuButton( 'Категории ДС', function() {
ruWikiQualityArticles.changeCategories();
} );
};
var changeCategories = this.changeCategories = function() {
$( "#ruWikiQAChangeCategoryForm" ).dialog( 'open' );
};
var changeCategoriesImpl = this.changeCategoriesImpl = function() {
var qaMessage = $( '#qa-message' );
var oldCategory1 = qaMessage.data( 'qa-category-1' );
var oldCategory2 = qaMessage.data( 'qa-category-2' );
var oldCategory3 = qaMessage.data( 'qa-category-3' );
var oldCategories = [ oldCategory1, oldCategory2, oldCategory3 ];
var newCategory1 = $( '#ruWikiQAChangeCategory1TextField' ).val();
var newCategory2 = $( '#ruWikiQAChangeCategory2TextField' ).val();
var newCategory3 = $( '#ruWikiQAChangeCategory3TextField' ).val();
var newCategories = [ newCategory1, newCategory2, newCategory3 ];
var funcs = funcArray(8);
var success = true;
var title = getCurrentPage();
var functionBuilderAdd = function( i ) {
return function() {
if ( newCategories[i] && $.inArray( newCategories[i], oldCategories ) == -1 ) {
ruWikiQualityArticles.addToCategory(newCategories[i], title, function() { success = false; })
.always(funcs[0 + i + 1]);
} else {
funcs[0 + i + 1]();
}
};
};
var functionBuilderRemove = function( i ) {
return function() {
if ( oldCategories[i] && $.inArray( oldCategories[i], newCategories ) == -1 ) {
ruWikiQualityArticles.removeFromCategory(oldCategories[i], title)
.fail(function() { success = false; })
.always( funcs[3 + i + 1] );
} else {
funcs[3 + i + 1]();
}
};
};
for ( var i = 0; i < 3; i++ ) {
funcs[0 + i] = functionBuilderAdd( i );
funcs[3 + i] = functionBuilderRemove( i );
}
funcs[6] = function() {
var newTemplate = makeDsTemplateRecord(newCategories);
pageGetEditSave(title, 'all', 'Обновление содержимого шаблона {{Добротная статья}} в тексте статьи…',
function(content, params) {
var newTemplate = params.template;
var patt = new RegExp( "\\{\\{Добротная статья[^\\}]*\\}\\}", "i" );
var newContent = content.replace(patt, newTemplate);
return newContent;
}, {template: newTemplate}, 'Обновление категорий [[ВП:ДС|добротной статьи]]', 'error', 'error')
.always(funcs[7]);
};
funcs[7] = function() {
if (success) {
finalize('Всё сделано', T_FINISHED_OK, 'info');
} else {
finalize('Завершено', T_FINISHED_ERR, 'error');
}
};
var finalize = function(title, message, status) {
ruWikiQualityArticles.reloadWithMessage(title, message, status);
};
funcs[0]();
};
var apiQueryLatestRevision = this.apiQueryLatestRevision = function( args ) {
args.action = 'query';
args.prop = 'revisions';
args.rvprop = 'content';
return new mw.Api().get( args );
};
var addToCategory = this.addToCategory = function( category, title, failAction ) {
var operation = 'Добавление статьи «' + title + '» в список статей категории «' + category + '»…';
mw.notify(operation, notifyOptOk);
return new mw.Api().postWithEditToken( {
action: 'edit',
title: PAGE_PREFIX_LIST + category,
summary: 'Добавление статьи «[[' + title + ']]» в список' + summarySuffix,
appendtext: '\n[[' + title + ']]',
} ).done( function() {
mw.notify(operation + T_OK, notifyOptOk);
} ).fail( function() {
console.log( arguments );
mw.notify(operation + T_ERR, notifyOptError);
if (failAction) { failAction(); }
} );
};
this.removeFromCategory = function( category, title ) {
var op = 'Удаление статьи «' + title + '» из служебного списка категории «' + category + '»…';
var summaryText = 'Удаление статьи «[[' + title + ']]» из списка категории «' + category + '»';
return pageGetEditSave(PAGE_PREFIX_LIST + category, 'all', op,
removeFromCategoryImpl, {title: title, category: category}, summaryText, 'error');
};
function removeFromCategoryImpl(content, params) {
var title = params.title;
var category = params.category;
var oldContent = content + '\n';
if (oldContent.indexOf( '\n[[' + title + ']]\n' ) === -1) {
mw.notify('Удаление статьи «' + title + '» из служебного списка категории «' + category + '»…'
+ T_ERR + ' Статья не найдена в служебном списке категории', notifyOptError);
return undefined;
}
var newListContent = oldContent.replace('\n[[' + title + ']]\n', '\n');
newListContent = newListContent.substring(0, newListContent.length - 1);
return newListContent;
}
this.purge = function() {
mw.notify( 'Перезагрузка страницы…', notifyOptOk);
new mw.Api().post( { action: 'purge', titles: getCurrentPage(true) } ).then(function () {
window.location.reload();
}, function () {
mw.notify( 'Не удалось перезагрузить страницу напрямую. Открываем страницу с подтверждением…', notifyOptOk);
window.location.assign( mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/index.php?action=purge&title='
+ encodeURIComponent( getCurrentPage(true) ) );
});
return;
};
function updateProjectTemplatesImpl(content, params) {
var newLevel = params.newLevel;
var patterns = [ "\\{\\{статья проекта[^\\}]*\\}\\}", "\\{\\{проект[^\\}]*\\}\\}" ];
var result = content;
for ( var i = 0; i < patterns.length; i++ ) {
var patt = new RegExp( patterns[i], "gi" );
result = result.replace( patt, function( found, offset, s ) {
var separatorIndex = found.indexOf( '\|' );
if ( separatorIndex > 0 ) {
if ( found.indexOf( 'уровень' ) > 0 ) {
return found.replace( new RegExp( 'уровень\\s*=\\s*[A-Za-z0-9А-Яа-я]*', 'i' ), 'уровень=' + newLevel );
} else { // ранее не было параметра уровень
return found.substring( 0, separatorIndex ) + '\|уровень=' + newLevel + found.substring( separatorIndex, found.length );
}
} else { // статья ранее не была оценена совсем
return found.replace( '}}', '\|уровень=' + newLevel + '\|важность=}}' );
}
} );
}
return result;
}
function updateProjectTemplates(articleTitle, newLevel) {
return pageGetEditSave(articleTitle, 0, 'Обновление шаблонов проектов… ',
updateProjectTemplatesImpl, {newLevel: newLevel}, 'Обновление шаблонов проектов', 'ignore');
};
function pageGetEditSave(title, section, operation, editFunc, params, summary, no_page, no_update, no_create) {
var d = $.Deferred();
var op1 = 'Получение текста страницы ' + title + '…';
mw.notify(operation + op1, notifyOptOk);
var qParams = {titles: title};
if (section === 0) qParams.rvsection = 0;
ruWikiQualityArticles.apiQueryLatestRevision(qParams)
.done( function( result ) {
var content = '';
var pageInfo = getFirstObjectValue( result.query.pages );
if ( !pageInfo.revisions || !pageInfo.revisions[0] || !pageInfo.revisions[0]['*'] ) {
if (no_page == 'error') {
mw.notify( operation + op1 + T_ERR + ' Страница не существует.', notifyOptError);
d.reject( 'Невозможно получить текст страницы ' + title);
return;
} else if (no_page == 'ignore') {
mw.notify( operation + ' Страница не существует. Обновление не требуется. ', notifyOptOk);
d.resolve();
return;
} else {
mw.notify(operation + op1 + T_OK + ' Страница ещё не создана. ', notifyOptOk);
}
} else {
mw.notify(operation + op1 + T_OK, notifyOptOk);
mw.notify(operation + 'Анализ текста страницы ' + title + '…', notifyOptOk);
content = pageInfo.revisions[0]['*'];
}
var newContent = editFunc(content, params);
if (content === undefined) {
d.reject();
return;
} else if (content === newContent ) {
if (no_update == 'error') {
mw.notify(operation + 'Что-то пошло не так. Обновление страницы ' + title + ' не удалось.', notifyOptError);
d.reject();
} else {
mw.notify(operation + 'Обновление страницы ' + title + ' не требуется.', notifyOptOk);
d.resolve();
}
return;
}
var op2 = 'Сохранение изменений страницы ' + title + '…';
mw.notify(operation + op2, notifyOptOk);
var summaryText = summary || title;
var qParams = {action: 'edit', title: title, summary: summaryText + summarySuffix, text: newContent};
if (section === 0) qParams.section = 0;
if (no_create == true) qParams.nocreate = true;
new mw.Api().postWithEditToken(qParams).done( function() {
mw.notify(operation + op2 + T_OK, notifyOptOk);
d.resolve();
} ).fail( function() {
mw.notify(operation + op2 + T_ERR, notifyOptError);
d.reject.apply( d, arguments );
} );
} ).fail( function() {
mw.notify( operation + op1 + T_ERR, notifyOptError);
d.reject.apply( d, arguments );
} );
return d.promise();
}
this.addNominateToCancellationButtons = function() {
if ( $( "#qa-message" ).length === 0 )
// не является ДС
return;
RuWikiQualityArticles.addToolboxMenuButton(
'Номинировать на лишение статуса ДС',
function() {
var nominateFormDiv = $( '<div id="ruWikiQualityNominateToCancellation" title="Выдвижение статьи на лишение статуса добротной">'
+ '<table border="0"><tr><td width="50" align="center">'
+ '<img src="' + COMMONS_UPLOAD + '/e/e5/Crystal_Clear_action_bookmark_Silver_doubt.svg/40px-Crystal_Clear_action_bookmark_Silver_doubt.svg.png" height="40" width="40"></td><td>'
+ '<p>Перед выдвижением на лишением статуса ознакомьтесь с <a href="https://ru.wikipedia.org/wiki/Википедия:ТДС">требованиями к добротным статьям</a></b>.<br/>'
+ 'Пожалуйста, не номинируйте <b>более 3 статей в день</b>. Если номинируете статью впервые, укажите это при номинировании и дождитесь итога по первой номинации, прежде чем действовать дальше.</p>'
+ '</td></tr></table>'
+ '<form><fieldset>'
+ '<textarea name="opiniontext" id="ruWikiQualityNominateToCancellationComment" style="height: 150px;" class="text ui-widget-content ui-corner-all"></textarea>'
+ '</fieldset></form>'
+ '<p class="validateTips" style="color:gray">Поле комментария обязательно к заполнению,<br/>ваша подпись будет добавлена автоматически.</p>'
+ '</div>' );
var nominateCommentField = nominateFormDiv.find( '#ruWikiQualityNominateToCancellationComment' );
nominateFormDiv.dialog( {
autoOpen: true,
height: 'auto',
width: 600,
modal: true,
buttons: {
"Номинировать на лишение статуса": function() {
var bValid = true;
nominateCommentField.removeClass( "ui-state-error" );
bValid = bValid && ruWikiQualityArticles.checkNotEmpty( nominateCommentField );
if ( bValid ) {
$( this ).dialog( "close" );
ruWikiQualityArticles.nominateToCancellation( nominateCommentField.val() );
}
},
"Отменить": function() {
$( this ).dialog( "close" );
}
}
} )
} );
};
this.nominateToCancellation = function( comment ) {
var replaceTemplateInArticle = this.nominateToCancellation_replaceTemplate( mw.config.get( 'wgTitle' ), '{{Добротная статья|', '{{К лишению статуса добротной|'
+ RuWikiQualityArticles.getCurrentDateWikitext() + '|' );
var addToDiscussionPage = this.nominateToCancellation_addToDiscussionPage( mw.config.get( 'wgTitle' ), comment );
$.when( replaceTemplateInArticle, addToDiscussionPage ).done( function() {
ruWikiQualityArticles.purge();
} );
};
this.nominateToCancellation_addToDiscussionPage = function( articleTitle, comment ) {
var operation = 'Номинирование на лишение статуса… Создание секции обсуждения… ';
mw.notify( operation, notifyOptOk);
return new mw.Api().postWithEditToken( {
action: 'edit',
title: PAGE_CANCEL_DISC,
section: 0,
appendtext: '\n\n== [[' + articleTitle + ']] ==\n' + comment + T_SIGN_A + '\n',
summary: 'Номинирование «[[' + articleTitle + ']]» на лишение статуса добротной' + RuWikiQualityArticles.summarySuffix,
} ).done( function() {
mw.notify( operation + T_OK, notifyOptOk);
} ).fail( function() {
mw.notify( operation + 'Неизвестная ошибка!', notifyOptError);
} );
};
this.nominateToCancellation_replaceTemplate = function( articleTitle, oldTemplateName, newTemplateName ) {
var d = $.Deferred();
var operation = 'Номинирование на лишение статуса… Замена шаблона добротной статьи… ';
var operation_p1 = 'Получение текста статьи… ';
var operation_p2 = 'Сохранение текста статьи… ';
mw.notify( operation + operation_p1, notifyOptOk);
new mw.Api().get( {
action: 'query',
prop: 'revisions',
rvprop: 'content',
titles: articleTitle,
} ).done( function( result ) {
var pageInfo = RuWikiQualityArticles.getFirstObjectValue( result.query.pages );
var content = pageInfo.revisions[0]['*'];
var newContent = content.replace( new RegExp( "(" + $.ui.autocomplete.escapeRegex( oldTemplateName ) + ")", "gi" ), newTemplateName );
mw.notify( operation + operation_p2, notifyOptOk);
new mw.Api().postWithEditToken( {
action: 'edit',
title: articleTitle,
summary: 'Номинирование на лишение статуса добротной' + RuWikiQualityArticles.summarySuffix,
text: newContent,
} ).done( function() {
mw.notify( operation + operation_p2 + T_OK, notifyOptOk);
d.resolve();
} ).fail( function() {
mw.notify( operation + operation_p2 + ' Неизвестная ошибка!', notifyOptError);
d.reject.apply( d, arguments );
} );
} ).fail( function() {
mw.notify( operation + operation_p1 + T_ERR, notifyOptError);
console.log( arguments );
d.reject.apply( d, arguments );
} );
return d.promise();
};
};
RuWikiQualityArticles.summarySuffix = ' с помощью гаджета QA (v. ' + mw.loader.moduleRegistry['ext.gadget.qualityArticles'].version + ')';
RuWikiQualityArticles.addToolboxMenuButton = function( label, click ) {
$( "#p-tb div ul" ).append( $( '<li class="plainlinks"></li>' ).append( $( document.createElement( 'a' ) ).text( label ).css( 'cursor', 'pointer' ).click( click ) ) );
};
RuWikiQualityArticles.getFirstObjectValue = function( obj ) {
"use strict";
return obj[Object.keys( obj )[0]];
};
RuWikiQualityArticles.getCurrentDateWikitext = function() {
"use strict";
return '{{su' + 'bst:CURRENTDAY}} {{su' + 'bst:CURRENTMONTHNAMEGEN}} {{su' + 'bst:CURRENTYEAR}}';
};
mw.loader.using( [ 'jquery.ui', 'mediawiki.api' ], function() {
var ruWikiQualityArticles = new RuWikiQualityArticles();
ruWikiQualityArticles.addButtonsDiscussion();
ruWikiQualityArticles.addButtonsArchive();
ruWikiQualityArticles.addButtonsNominate();
ruWikiQualityArticles.addButtonsChangeCategories();
ruWikiQualityArticles.addNominateToCancellationButtons();
ruWikiQualityArticles.addFinalDialog();
}, function () {
console.log('Dependency Error! DS may not work!');
mw.notify( 'Ошибка при загрузке зависимостей. Гаджет может не работать, или работать с ошибками!',
{ autoHide: false, type: 'error', tag: 'QA-Gadget' } );
} );
// </nowiki>