Меню

Главная
Случайная статья
Настройки
Участник:MBH/Черновик
Материал из https://ru.wikipedia.org

// /* Скрипт автоматизирует ряд действий для проекта Добротные статьи: 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://upload.wikimedia.org/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; } // mw-editsection-like -> убираем кнопку "подписаться", которая по каким-то причинам, завёрстана внутрь span.mw-headline var sectionTitle = mw.html.escape( jThis.find( "h2" ).clone().children( "h2" ).remove().end().text() ); console.log('Section title: «' + sectionTitle + '»'); var sectionIndexStr; jThis.find( "span.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( '  Проверить плагиат' + ' ' ) .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#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 = 'Комментарий к оценке ' + 'За для статьи «' + sectionTitle + '»:'; } else if ( opinionType == 'Против' ) { addOpinionFormTitle = 'Комментарий к оценке ' + 'Против для статьи «' + 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#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 = 'Комментарий при избрании статьи:'; $( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle ); $( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Требованиям [[ВП:ТДС]] соответствует.' ); } if ( summaryType == "yes" ) { summaryFormDivDesc.text( 'Шаблон {{Сделано|Статья избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' ); summaryText = 'Статья «[[' + sectionTitle + ']]» избрана'; newStatus = 'accepted'; $( "span.ruWikiSummaryCategorySpan" ).show(); } if ( summaryType == "togood" ) { summaryFormDivDesc.text( 'Шаблон {{Сделано|Статья избрана и рекомендована в хорошие}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' ); summaryText = 'Статья «[[' + sectionTitle + ']]» избрана и рекомендована в хорошие'; newStatus = 'accepted'; $( "span.ruWikiSummaryCategorySpan" ).show(); } if ( summaryType == "no" ) { summaryFormTitle = 'Комментарий при отказе в статусе:'; $( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle ); $( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Подробная причина отказа со ссылками на требования ВП:ТДС' ); summaryFormDivDesc.text( 'Шаблон {{Не сделано|Статья не избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' ); summaryText = 'Статья «[[' + sectionTitle + ']]» НЕ избрана'; newStatus = 'declined'; $( "span.ruWikiSummaryCategorySpan" ).hide(); } if ( summaryType == "toobig" ) { summaryFormTitle = 'Комментарий при отказе в статусе из-за большого размера:'; $( "#ruWikiSummaryFormSummaryTitle" ).html( summaryFormTitle ); summaryFormDivDesc.text( 'Шаблон {{Не сделано|Статья не избрана}} и подпись участника (' + T_SIGN + ') будут добавлены автоматически ' ); $( "#ruWikiSummaryFormSummaryText" ).attr( 'placeholder', 'Подробная причина отказа со ссылками на требования ВП:ТДС' ); summaryText = 'Статья «[[' + sectionTitle + ']]» НЕ избрана'; newStatus = 'declined'; $( "span.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( '
' + '
' + '' + '

Успех

' + '

Страница будет перезагружена через 10 секунд

' + '
' + '
' ); 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(''); var SECONDS_COUNT = 5; if (status=='error') { SECONDS_COUNT = 10; // Больше времени чтобы юзер успел обратить внимание и прочитать } var countDownText = formDiv.find( '#ruWikiQualityCountDownText' ); countDownText.html('Страница будет перезагружена через ' + SECONDS_COUNT + ' секунд'); var myCounter = new Countdown({ seconds:SECONDS_COUNT, // number of seconds to count down onUpdateStatus: function(sec){ console.log(sec); countDownText.html('Страница будет перезагружена через ' + sec + ' секунд'); }, 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( '
' + '
' + '' + '

Перед выдвижением ознакомьтесь с требованиями к добротным статьям.
' + 'Пожалуйста, не номинируйте более 3 статей в день. Если номинируете статью впервые, укажите это при номинировании и дождитесь итога по первой номинации, прежде чем действовать дальше.

' + '
' + '
' + '' + '
' + '

Поле комментария обязательно к заполнению,
ваша подпись будет добавлена автоматически.

' + '
' ); 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() { $( "span.ruWikiQualityCandatatesToArchivButton" ).before( "
" ).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{{Генератор таблицы КДС|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( '
' + '
' + '
' + '' + '' + '
' + '
' + '
' + '
' ); $( "#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 = $( '
' + '
' + '' + '

Перед выдвижением на лишением статуса ознакомьтесь с требованиями к добротным статьям.
' + 'Пожалуйста, не номинируйте более 3 статей в день. Если номинируете статью впервые, укажите это при номинировании и дождитесь итога по первой номинации, прежде чем действовать дальше.

' + '
' + '
' + '' + '
' + '

Поле комментария обязательно к заполнению,
ваша подпись будет добавлена автоматически.

' + '
' ); 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( $( '' ).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' } ); } ); //
Downgrade Counter