Модуль:RedirectChecker

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Документация

Используется в {{Перенаправление на раздел}}. Автоматически проверяет текущую цель перенаправления на существование в исходной статье по следующим критериям:

  • == Заголовок == — в коде страницы и при неудачной попытке в девикифицированном коде.
  • id="Якорь" — в отпаршенном коде страницы среди шаблонов якорей, указанных в конфигурации.
  • <hX>Заголовок</hX> — в отпаршенном коде страницы.

Конфигурация для русского раздела указана в Модуль:RedirectChecker/config.json.

Функции

  • p._parse может использоваться для передачи любого кода на проверку. В основном полезно при тестировании алгоритма модуля.
  • p._main — функция для вызова из других модулей.
  • p.main — функция для вызова из шаблонов в формате
    {{#invoke:RedirectChecker|main}}
    
--
-- Checks redirects to sections for their target
-- Also checks if a page is a redirect page
--
-- Adds tracking categories in both cases
--
require( 'strict' )
local p = {}

local getArgs = require( 'Module:Arguments' ).getArgs
local delink
local escapePattern = require( 'Module:String' )._escapePattern

-- [[Module:RedirectChecker/config.json]]
local config = mw.loadJsonData( 'Module:RedirectChecker/config.json' )

local anchorTemplates = config[ 'anchorTemplates' ]

local function isEmpty( str )
	return str == nil or str == ''
end

local function getConfigVal( key )
	if not isEmpty( config[ key ] ) then
		return config[ key ]
	end
	
	return error( 'RedirectChecker: config needs to be fixed.' )
end

-- [[Module:RedirectChecker/config.json]] has templates that create IDs in a list
local function isValidTemplate( str )
	if type( anchorTemplates ) ~= 'table' then
		return false
	end
	
	for i, val in ipairs( anchorTemplates ) do
		if mw.ustring.find( str, '{{' .. val ) ~= nil then
			return true
		end
	end
	
	return false
end

local function getError( str )
	return string.format( '<div class="error"><strong>%s</strong></div>[[Category:%s]]', str, getConfigVal( 'errorCat' ) )
end

local function escape( text )
	-- Account for &nbsp; (or Unicode variant of it)
	return ( escapePattern( text ):gsub( '&nbsp;', ' ' ):gsub( '%s', '%%s' ) )
end

local function escapeRepl( text )
	-- Replace % to %% to avoid %1, %2, %3 being treated as groups
	return ( text:gsub( '%%', '%%%' ) )
end

-- Separate function for testing
function p._parse( anchor, content, frame )
	if frame == nil then
		frame = mw.getCurrentFrame()
	end
	if isEmpty( content ) then
		return false
	end
	
	-- Find a heading of any level matching anchor
	local headingPattern = '=%s*' .. escape( anchor ) .. '%s*='
	local heading = mw.ustring.match( content, headingPattern )
	if heading ~= nil then
		return true
	end
	
	-- Convert all headings to plain text and try again
	delink = require( 'Module:Delink' )._delink
	for capture in mw.ustring.gmatch( content, '\n=+[^\n]+=+' ) do
		-- Convert replacement only into plain text
		local before = escapeRepl( capture )
		local r = before:gsub( '%<%/?su[pb]%>', '' )
			:gsub( '(=+)%s*\'+(.-)\'+%s*%1', '%1 %2 %1' )
		
		if capture:find( '%[' ) or capture:find( '%{' ) then
			r = frame:preprocess( r )
			r = mw.text.killMarkers( r )
			r = delink( { r } )
		end

		if before ~= r then
			content = mw.ustring.gsub( content, escape( capture ), r )
		end
	end
	
	heading = mw.ustring.match( content, headingPattern )
	if heading ~= nil then
		return true
	end
	
	-- Preprocess templates and try to find an ID
	if type( anchorTemplates ) == 'table' then
		for capture in content:gmatch( '{{[^}]+}}' ) do
			if isValidTemplate( capture ) then
				content = mw.ustring.gsub( content, escape( capture ), escapeRepl( frame:preprocess( capture ) ) )
			end
		end
		content = mw.text.killMarkers( content )
	end
		
	local id = mw.ustring.match( content, ' id="?' .. escape( anchor ) .. '"?' )
	if id ~= nil then
		return true
	end
	
	-- Try to find HTML heading tag
	local hX = mw.ustring.match( content, '%<(h[1-6])%>%s*' .. escapePattern( anchor ) .. '%s*</%1>' )
	if hX ~= nil then
		return true
	end
	
	return false
end

function p._main( page, frame )
	local mwTitle = mw.title.new( page )
	if mwTitle == nil or not mwTitle.exists then
		return false, getError( string.format( getConfigVal( 'errorMissing' ), page ) )
	end
	
	local target = mwTitle.redirectTarget
	if target == false then
		return false, getError( string.format( getConfigVal( 'errorNotRedirect' ), page ) )
	end
	
	local anchor = target.fragment
	if isEmpty( anchor ) then
		return false, getError( string.format( getConfigVal( 'errorNoSection' ), page ) )
	end
	
	return p._parse( anchor, target:getContent(), frame )
end

function p.main( frame )
	local args = getArgs( frame )
	
	local result, err = p._main( args.page or mw.title.getCurrentTitle().fullText, frame )
	if result == true then
		return ''
	end
	
	return string.format( '%s[[Category:%s]]', err or '', getConfigVal( 'brokenSectionCat' ) )
end

return p