Модуль:Get page content
Поделись знанием:
Документация
Реализация шаблона {{вставить раздел}}, но может возвращать информацию данными. В этом качестве активно используется модулем Topic monitoring. Модуль может быть дополнен другими функциями.
См. также
Во избежание поломок страниц, использующих данный модуль, желательно экспериментировать в Песочнице для модулей.
local p = {} local monthNames = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'} if not getArgs then getArgs = require('Модуль:Arguments').getArgs end local pageTitle, pageTitleText, sectionHeading, _frame --[=[ Helper function that escapes all pattern characters so that they will be treated as plain text. Copied from [[:en:Module:String]]. ]=] local function escapePattern(pattern_str) return mw.ustring.gsub(pattern_str, '([%(%)%.%%%+%-%*%?%[%^%$%]])', '%%%1') end local function cleanSectionHeading(heading) -- The following patterns reproduce [[Участник:Jack who built the house/transferHeadingToSummary.js]] heading = mw.ustring.gsub(heading, '%[%[:?[^|]*|([^%]]*)%]%]', '%1') heading = mw.ustring.gsub(heading, '%[%[:?([^%]]*)%]%]', '%1') heading = mw.ustring.gsub(heading, "'''(.-)'''", '%1') heading = mw.ustring.gsub(heading, "''(.-)''", '%1') heading = mw.ustring.gsub(heading, '<%w+ ?/?>', '') heading = mw.ustring.gsub(heading, '<%w+ [%w ]-=[^<>]->', '') heading = mw.ustring.gsub(heading, '</%w+ ?>', '') heading = mw.ustring.gsub(heading, ' +', ' ') heading = mw.text.trim(heading) return heading end local function sectionHeadingToLink(sectionHeading) local sectionHeadingLink = sectionHeading -- The following reproduces processURI function of [[Участник:Jack who built the house/copyWikilinks.js]] --[[sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '<', '%%3C') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '>', '%%3E') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '%[', '%%5B') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '%]', '%%5D') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '{', '%%7B') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '|', '%%7C') sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, '}', '%%7D')]] sectionHeadingLink = mw.ustring.gsub(sectionHeadingLink, ' ', '.C2.A0') return '#' .. sectionHeadingLink end local function expandTemplate(tname, targs) local success, result = pcall( _frame.expandTemplate, _frame, {title = tname, args = targs} ) return success and result or '' end local function getLastMatchWith2Captures(s, pattern) local iterator = mw.ustring.gmatch(s, pattern) local lastCapture1, lastCapture2 for capture1, capture2 in iterator do lastCapture1 = capture1 lastCapture2 = capture2 end return lastCapture1, lastCapture2 end local function killHeadingMarkers(content) content = mw.ustring.gsub( content, string.char(127) .. '\'"`UNIQ%-%-h%-%d+%-%-QINU`"\'' .. string.char(127), '' ) return content end local function parsePage(args, talkpageMode) if not _frame then _frame = mw.getCurrentFrame() end local yesno = require('Module:Yesno') local afdMode = yesno(args['режим КУ'], false) local pageListMode = yesno(args['режим списков страниц'], false) local standardMode if not talkpageMode and not afdMode and not pageListMode then standardMode = true end local returnAsData = not standardMode or yesno(args['как данные'], false) local returnAsData_onlyStats if returnAsData and args['только статистика'] then returnAsData_onlyStats = yesno(args['только статистика'], false) else returnAsData_onlyStats = false end local subsectionHeading, resultMode if standardMode then resultMode = yesno(args['итог'], false) subsectionHeading = args['подраздел'] end local standardHeading = not standardMode or yesno(args['стандартный заголовок'], false) local shortHeadings = talkpageMode and yesno(args['короткие заголовки'], false) local sectionHeadingLink if (standardHeading or returnAsData_onlyStats) and not talkpageMode then sectionHeadingLink = sectionHeadingToLink(sectionHeading) end local withoutHeading, standardSectionHeading if standardHeading then withoutHeading = true if afdMode then standardSectionHeading = sectionHeading .. ' <span style="font-size:80%;">← [[' .. pageTitleText .. sectionHeadingLink .. '|' .. pageTitleText .. ']]</span>' elseif not talkpageMode then standardSectionHeading = '[[' .. pageTitleText .. sectionHeadingLink .. '|' .. ( subsectionHeading and subsectionHeading .. ' (' .. sectionHeading .. ')' or (sectionHeading == 'top' and '<i>Преамбула</i>' or sectionHeading) ) .. ']] <span style="font-size:80%;">← ' .. pageTitleText .. '</span>' end elseif not talkpageMode and args['заголовок'] then withoutHeading = not yesno(args['заголовок'], false) else withoutHeading = false end local level = tonumber(args['уровень']) or 2 -- базовый уровень при вставке разделов local additionalEqualSigns = '' if level > 2 then for i = 3, level do additionalEqualSigns = additionalEqualSigns .. '=' end end local showInTOC if args['показывать в содержании'] then showInTOC = yesno(args['показывать в содержании'], true) else showInTOC = true end local showStatsLine if standardHeading and args['строка статистики'] then showStatsLine = yesno(args['строка статистики'], false) else showStatsLine = false end --local additionalLabels = afdMode or args['дополнительные ярлыки'] local sectionContent local data = {} local allData = talkpageMode and {} if pageTitle.exists then local fullContent = (talkpageMode and '' or '\n== top ==') .. '\n' .. pageTitle:getContent() .. '\n= technical heading =\n' fullContent = mw.ustring.gsub('\n' .. fullContent, '(\n(=+)[^\n]-%2[ \t\f\v]*)<!%-%-.-%-%->', '%1') local iterator local rootSectionID if talkpageMode then -- соответствие заголовков второго уровня заголовкам вообще для talkpageMode rootSectionID = {} iterator = mw.ustring.gmatch(fullContent, '\n(=+)[^\n]-%1\n') local i, j = 0, 0 for equalSigns in iterator do i = i + 1 if equalSigns == '==' then j = j + 1 rootSectionID[j] = i end end iterator = mw.ustring.gmatch(fullContent, '\n((==)([^=][^\n]-)==)') else iterator = mw.ustring.gmatch(fullContent, '\n((=+)([^\n]-)%2)') end local headingNum = 0 for fullMatch, equalSigns, match in iterator do headingNum = headingNum + 1 data = {} match = mw.text.trim(match) local thisHeading = mw.text.encode(cleanSectionHeading(match), '<>%[%]{|}') if thisHeading == sectionHeading or talkpageMode then -- получаем содержимое раздела local equalSignsPattern = '=' for i = 2, #equalSigns do equalSignsPattern = equalSignsPattern .. '=?' end sectionContent = mw.ustring.match(fullContent, (withoutHeading and '' or '(') .. escapePattern(fullMatch) .. '[ \t\f\v]*\n+' .. (withoutHeading and '(' or '') .. '.-)\n' .. equalSignsPattern .. '[^=][^\n]-=+\n') or '' --will fail at "===" or the like sectionContent = sectionContent:gsub('<onlyinclude>.-</onlyinclude>', '') sectionContent = sectionContent:gsub('<includeonly>(.-)</includeonly>', '%1') local subsectionHeadingNum if subsectionHeading then -- withoutHeading тут неактуально — standardHeading всегда true local beforeContent beforeContent, sectionContent = mw.ustring.match(sectionContent .. '\n= technical heading =\n', '(.-)\n' .. equalSigns .. '=[ \t\f\v]*' .. escapePattern(subsectionHeading) .. '[ \t\f\v]*=+[ \t\f\v]*\n+(.-)\n' .. equalSignsPattern .. '[^=][^\n]-=+[ \t\f\v]*\n') subsectionHeadingNum = 0 for match2 in mw.ustring.gmatch('\n' .. beforeContent, '\n=+[^\n]-=+') do subsectionHeadingNum = subsectionHeadingNum + 1 end if not sectionContent then sectionContent = '' end end local closureHeading, resultMode_closureHeadingNum if resultMode then local beforeContent, newSectionContent local closureTitles = {'Итог', 'Окончательный итог', 'Автоитог'} if withoutHeading and not standardHeading then for i = 1, #closureTitles do -- через or несколько match'ей тут нельзя, так как возвращаются несколько значений if i == 1 or not newSectionContent then beforeContent, closureHeading, newSectionContent = mw.ustring.match(sectionContent .. '\n= technical heading =\n', '(.*)\n' .. equalSigns .. '=[ \t\f\v]*(' .. closureTitles[i] .. '[^\n]-)[ \t\f\v]*=+[ \t\f\v]*\n+(.-)\n' .. equalSignsPattern .. '[^=][^\n]-=+[ \t\f\v]*\n') end end else for i = 1, #closureTitles do -- через or несколько match'ей тут нельзя, так как возвращаются несколько значений if i == 1 or not newSectionContent then beforeContent, newSectionContent, closureHeading = mw.ustring.match(sectionContent .. '\n= technical heading =\n', '(.*)\n(' .. equalSigns .. '=[ \t\f\v]*(' .. closureTitles[i] .. '[^\n]-)[ \t\f\v]*=+[ \t\f\v]*\n.-)\n' .. equalSignsPattern .. '[^=][^\n]-=+[ \t\f\v]*\n') end end end if newSectionContent then sectionContent = newSectionContent newSectionContent = nil resultMode_closureHeadingNum = 0 for match2 in mw.ustring.gmatch('\n' .. beforeContent, '\n=+[^\n]-=+') do resultMode_closureHeadingNum = resultMode_closureHeadingNum + 1 end else if not returnAsData_onlyStats then sectionContent = "''Похоже, что итога пока нет.''" else sectionContent = '' end end end -- извлекаем данные: -- о сообщениях if showStatsLine or returnAsData then local iterator2 = mw.ustring.gmatch('\n' .. sectionContent, '\n([^\n]+)((%d%d):(%d%d), (%d%d?) (%w+) (%d%d%d%d)) %(UTC%)') --(%d%d? %w+ %d%d%d%d) local msgCount, authors = 0, {} local lastMsgDate, lastMsgDateString, lastMsgAuthor for precedingText, fullDateString, h, i, d, mesyats, y in iterator2 do msgCount = msgCount + 1 h = tonumber(h) i = tonumber(i) d = tonumber(d) y = tonumber(y) local m = 1 for k, v in pairs(monthNames) do if v == mesyats then m = k end end local patterns = { '()%[%[[УуUu]:([^|%]#]+)', '()%[%[[Уу]частни[кц]а?:([^|%]#]+)', '()%[%[[Uu]ser:([^|%]#]+)', '()%[%[ОУ:([^|%]#]+)', -- Мастер теней '()%[%[Обсуждение участника:([^|%]#]+)', -- Abba8 '()%[%[Special:Contributions/([^|%]#]+)', -- анонимы '()%[%[[^|]+|([^%]]+)%]%]' -- случаи типа [[w:en:Wikipedia:TWL/Coordinators|The Wikipedia Library Team]] } local lastPos = 0 local author for k, v in pairs(patterns) do pos, mention = getLastMatchWith2Captures(precedingText, v) if pos and pos > lastPos then lastPos = pos author = mention end if k == 6 and author then break end -- чтобы «обс» / «вкл» и т. п. из последнего паттерна -- не вытесняли ники end if author then author = mw.text.encode(author) end if not lastMsgDate or (y > lastMsgDate.y) or (y == lastMsgDate.y and m > lastMsgDate.m) or (y == lastMsgDate.y and m == lastMsgDate.m and d > lastMsgDate.d) or (y == lastMsgDate.y and m == lastMsgDate.m and d == lastMsgDate.d and h > lastMsgDate.h) or (y == lastMsgDate.y and m == lastMsgDate.m and d == lastMsgDate.d and h == lastMsgDate.h and i > lastMsgDate.i) then lastMsgDate = {y = y, m = m, d = d, h = h, i = i} lastMsgDateString = fullDateString lastMsgAuthor = author or '?' end local found = false for k, v in pairs(authors) do if v == author then found = true break end end if not found then table.insert(authors, author) end end --local authorsList = table.concat(authors, ', ') if msgCount > 0 then if returnAsData then data.msgCount = msgCount --data.lastMsgDate = lastMsgDate data.lastMsgDateString = lastMsgDateString data.lastMsgDateTimestamp = os.time({year = lastMsgDate.y, month = lastMsgDate.m, day = lastMsgDate.d, hour = lastMsgDate.h, min = lastMsgDate.i}) data.lastMsgAuthor = lastMsgAuthor data.lastMsgAnchor = string.format('%.4d%.2d%.2d%.2d%.2d_%s', lastMsgDate.y, lastMsgDate.m, lastMsgDate.d, lastMsgDate.h, lastMsgDate.i, lastMsgAuthor) data.authors = authors end if showStatsLine then sectionContent = '\'\'Последнее сообщение: ' .. lastMsgDateString .. ' от ' .. lastMsgAuthor .. '. Всего ' .. msgCount .. ' {{plural: ' .. msgCount .. '|сообщение|сообщения|сообщений}} от ' .. #authors .. ' {{plural: ' .. #authors .. '|автора|авторов}}.\'\'\n' .. sectionContent end end end -- ...для ярлыков local preclosureHeading, challengedClosureHeading, partialClosureHeading, warningHeading if afdMode then closureHeading = mw.ustring.match(match, '^<s>.*') elseif pageListMode then warningHeading = mw.ustring.match(match, '^Предупреждение.*') or mw.ustring.match(match, '^Блокировка.*') or mw.ustring.match(match, '^Нарушение авторских прав.*') or mw.ustring.match(match, '^Нарушение АП.*') end local requestForPermission = false if mw.ustring.find(pageTitleText, '^Википедия:Заявки на статус') or mw.ustring.find(pageTitleText, '^Википедия:Заявки кандидатов в') then requestForPermission = true local closureContent closureHeading, closureContent = mw.ustring.match(sectionContent .. '\n= technical heading =\n', '\n===[ \t\f\v]*(Итог[^\n]-)[ \t\f\v]*=+[ \t\f\v]*\n+(.-)\n==?=?[^=][^\n]-=+[ \t\f\v]*\n') if closureContent then closureContent = closureContent :gsub('<includeonly>.-</includeonly>', '') :gsub('<!%-%-.-%-%->', '') closureContent = mw.text.trim(closureContent) if #closureContent < 5 then closureHeading = nil end end end -- экономим ресурсы и совершаем максимум два действия за один обход регуляркой: -- извлечение данных для ярлыков и формирование заголовков с корректной ссылкой на правку local i = 0 sectionContent = mw.ustring.gsub('\n' .. sectionContent, '\n((=+)([^\n]-)%2)', function (fullMatch2, equalSigns2, match2) i = i + 1 match2 = mw.text.trim(match2) if returnAsData and not resultMode and not requestForPermission then if not closureHeading and #equalSigns2 == #equalSigns + 1 then closureHeading = mw.ustring.match(match2, '^Итог.*') or mw.ustring.match(match2, '^Окончательный итог.*') or mw.ustring.match(match2, '^Автоитог.*') --or mw.ustring.match(match2, '^Автоматический итог.*') if closureHeading then challengedClosureHeading = nil preclosureHeading = nil partialClosureHeading = nil end end if not closureHeading then -- not closureHeading and additionalLabels if afdMode and not partialClosureHeading and #equalSigns2 == #equalSigns + 2 then partialClosureHeading = mw.ustring.match(match2, '^Итог.*') or mw.ustring.match(match2, '^Окончательный итог.*') or mw.ustring.match(match2, '^Автоитог.*') or mw.ustring.match(match2, '^Автоматический итог.*') end if not challengedClosureHeading and #equalSigns2 == #equalSigns + 1 then challengedClosureHeading = mw.ustring.match(match2, '^Оспоренный [иИ]тог.*') or mw.ustring.match(match2, '^Опротестованный [иИ]тог.*') --or mw.ustring.match(match2, '^Не [иИ]тог.*') if challengedClosureHeading then preclosureHeading = nil partialClosureHeading = nil end end if not challengedClosureHeading and #equalSigns2 == #equalSigns + 1 then preclosureHeading = mw.ustring.match(match2, '^Предварительный итог.*') or mw.ustring.match(match2, '^Пред[иы]тог.*') end end if closureHeading and #equalSigns2 == #equalSigns + 1 then challengedClosureHeading = mw.ustring.match(match2, '^Оспаривание [иИтога]*') if challengedClosureHeading then closureHeading = nil end end end local editlink2 = pageTitle:fullUrl('action=edit§ion=' .. (talkpageMode and rootSectionID[headingNum] + i or headingNum - 1 + i) + (not withoutHeading and -1 or 0) + (resultMode_closureHeadingNum and resultMode_closureHeadingNum or 0) + (subsectionHeadingNum and subsectionHeadingNum or 0) ) -- «- 1», так как, за исключением talkpageMode, мы вставляем искусственную первую секцию return '\n' .. expandTemplate('fake heading', { ['sub'] = #equalSigns2 + level - 2, match2, ['real heading'] = showInTOC and additionalEqualSigns .. fullMatch2 .. additionalEqualSigns or '', ['edit link'] = editlink2 }) end) if returnAsData then data.closureHeading = closureHeading data.preclosureHeading = preclosureHeading data.challengedClosureHeading = challengedClosureHeading data.partialClosureHeading = partialClosureHeading data.warningHeading = warningHeading end local thisHeadingLink = sectionHeadingLink and sectionHeadingLink or sectionHeadingToLink(thisHeading) if returnAsData then data.sectionHeadingLink = thisHeadingLink end -- формируем заголовок if not returnAsData_onlyStats and standardHeading then if talkpageMode then if shortHeadings then standardSectionHeading = '[[' .. pageTitleText .. thisHeadingLink .. '|' .. thisHeading .. ']]' else standardSectionHeading = '[[' .. pageTitleText .. thisHeadingLink .. '|' .. thisHeading .. ']] <span style="font-size:80%;">← ' .. pageTitleText .. '</span>' end elseif afdMode then standardSectionHeading = match .. ' <span style="font-size:80%;">← [[' .. pageTitleText .. thisHeadingLink .. '|' .. pageTitleText .. ']]</span>' end local editlink editlink = pageTitle:fullUrl('action=edit§ion=' .. (talkpageMode and rootSectionID[headingNum] or headingNum - 1)) sectionContent = expandTemplate('fake heading', { ['sub'] = 2 + level - 2, standardSectionHeading, ['real heading'] = showInTOC and additionalEqualSigns .. '== ' .. standardSectionHeading .. ' ==' .. additionalEqualSigns or '', ['edit link'] = editlink }) .. '\n' .. sectionContent end sectionContent = mw.text.trim(sectionContent) if talkpageMode then data.sectionHeading = thisHeading if not returnAsData_onlyStats then data.sectionContent = sectionContent end else break end end if talkpageMode and data.sectionHeading then table.insert(allData, data) end end if not talkpageMode and (returnAsData or not returnAsData_onlyStats) then if sectionContent == '' then if subsectionHeading then local sectionNotExistMessage = 'На странице [[' .. pageTitleText .. ']] в разделе «' .. sectionHeading .. '» не найден подраздел «' .. subsectionHeading .. '». Возможно, тема была заархивирована.' if returnAsData then data.sectionNotExistMessage = sectionNotExistMessage end if not returnAsData_onlyStats then sectionContent = '== ' .. standardSectionHeading .. " ==\n''" .. sectionNotExistMessage .. "''" end elseif not returnAsData_onlyStats then sectionContent = '== ' .. standardSectionHeading .. " ==\n''<span class=\"error\">Не удалось получить содержимое раздела «' .. sectionHeading .. '» на странице [[' .. pageTitleText .. ']] (хотя он был найден).</span>''" end end if not sectionContent then local sectionNotExistMessage = 'На странице [[' .. pageTitleText .. ']] не найден раздел «' .. sectionHeading .. '».' if not ( mw.ustring.find(pageTitleText, '^Википедия:К удалению/') or mw.ustring.find(pageTitleText, '^Википедия:К восстановлению/') or mw.ustring.find(pageTitleText, '^Википедия:К переименованию/') or mw.ustring.find(pageTitleText, '^Википедия:К объединению/') or mw.ustring.find(pageTitleText, '^Википедия:К разделению/') or mw.ustring.find(pageTitleText, '^Википедия:К улучшению/') ) then sectionNotExistMessage = sectionNotExistMessage .. ' Возможно, он был заархивирован.' end if returnAsData then data.sectionNotExistMessage = sectionNotExistMessage end if not returnAsData_onlyStats then sectionContent = (standardHeading and '== ' .. standardSectionHeading .. ' ==\n' or '') .. "''" .. sectionNotExistMessage .. "''" end end end else local pageNotExistMessage = 'Страницы [[' .. pageTitleText .. ']] не существует.' if not talkpageMode and not returnAsData_onlyStats then sectionContent = (standardHeading and '== ' .. standardSectionHeading .. ' ==\n' or '') .. "''" .. pageNotExistMessage .. "''" end if returnAsData then if talkpageMode then allData.pageNotExistMessage = pageNotExistMessage else data.pageNotExistMessage = pageNotExistMessage end end end if returnAsData then if talkpageMode then return allData else if not returnAsData_onlyStats then data.sectionContent = sectionContent end return data end else return sectionContent end end function p._parse_section_content(args) if not args[1] then return '' end if type(args[1]) ~= 'table' and args[1]:find('#') then pageTitle, sectionHeading = args[1]:match('(.-)#(.+)') if pageTitle then pageTitle = mw.title.new(pageTitle) end else pageTitle = type(args[1]) == 'table' and args[1] or mw.title.new(args[1]) sectionHeading = args[2] end if pageTitle then pageTitle.fragment = '' pageTitleText = pageTitle.prefixedText end -- Получить содержимое той страницы, на которой мы находимся, нельзя, так что -- в отсутствие заголовка использовать название текущей страницы мы не можем. if not pageTitle or pageTitleText == mw.title.getCurrentTitle().prefixedText or not sectionHeading then return '' end return parsePage(args, false) end function p._parse_talkpage_content(args) pageTitle = type(args[1]) == 'table' and args[1] or mw.title.new(args[1]) if pageTitle then pageTitle.fragment = '' pageTitleText = pageTitle.prefixedText end -- Получить содержимое той страницы, на которой мы находимся, нельзя, так что -- в отсутствие заголовка использовать название текущей страницы мы не можем. if not pageTitle or pageTitleText == mw.title.getCurrentTitle().prefixedText then return '' end return parsePage(args, true) end function p.parse_section_content(frame) _frame = frame local args = getArgs(frame) return killHeadingMarkers(frame:preprocess(p._parse_section_content(args))) end --[[function p.parse_talkpage_content(frame) _frame = frame local args = getArgs(frame) return killHeadingMarkers(frame:preprocess(p._parse_talkpage_content(args))) end]] return p