Модуль:Вложенный список (Bk;rl,&Flk'yuudw vhnvkt)

Перейти к навигации Перейти к поиску
Документация

Данный модуль реализует шаблон {{Вложенный список}}. После перехода на модуль стало возможно заменить старый код шаблона на более доступный и семантичный и начать отслеживать ошибки в коде выводимых страниц.

Код вызова модуля: {{#invoke:Вложенный список|main}}.

Категории

[[Категория:Википедия:Страницы с ошибками шаблона Вложенный список]] — ставится в случаях:

  • несуществования страницы или отсутствия текста при попытке вставить вложенный список (например, в случае некорректной разметки в нём).
  • наличия технических ошибок «Медиавики» в выводе вложенного списка.
  • наличия в выводе шаблонов:
  • ошибок в разметке списков с точки зрения вики-кода и доступности:
    • начала вложенного списка не с разметки элемента списка *.
    • вложенных списков с неправильной вложенностью (через :* или с ** или с ; в начале).
  • вложенных списков с заголовками 1-го и 2-го уровня (==) в выводе.
  • вложенных списков с вертикальной чертой (----) в выводе.
  • вложенных списков, в которых вложенные списки не входят в подсписки ({{NL|Пример}} вместо * {{NL|Пример}}).

[[Категория:Википедия:Страницы с шаблоном Вложенный список без ссылок]] ставится в случаях, когда в коде вложенного списка нет ссылок на другие страницы.

require( 'strict' )
--
-- Implements {{Вложенный список}} in one module
-- Allows us to potentially move towards a more accessible markup for the template when the time is right
-- Previously, the template broke the lists {{Вложенный список}} or {{NL2}} or {{NL3}} are in
-- Heavily borrows from https://en.wikipedia.org/wiki/Module:Excerpt_slideshow
--
local getArgs = require( 'Module:Arguments' ).getArgs

local templatePageName = 'Вложенный список'
local modulePageName = 'Вложенный список'
local disambigPageSuffix = ' (значения)'
local editLinkText = 'править'

local errorCat = 'Википедия:Страницы с ошибками шаблона Вложенный список'
-- Remove the variable to stop tracking this category
local linklessCat = 'Википедия:Страницы с шаблоном Вложенный список без ссылок'

local noTitle = 'Нет названия страницы.'
local invalidTitle = 'Неправильное название страницы <code>%s</code>.'
local noContent = 'Ошибка при включении страницы «[[%s]]».'
local noLinks = 'На странице «[[%s]]» нет ссылок на другие страницы.'
local notDisambig = 'На странице «[[%s]]» находится статья.'

-- Adds a replacement to a redirected variant when substed
local substWithRedirects = true

local moduleInvocationCode = '{{#invoke:' .. modulePageName .. '|main|'

local p = {}

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

local function addWarning( title, message )
	if title ~= nil and message == nil then
		message = string.format( noContent, title )
	end
	if title ~= nil and message ~= nil then
		message = string.format( message, title )
	end
	mw.addWarning( string.format( '[[Template:%s|%s]]: %s', templatePageName, templatePageName, message ) )
end

local function templatePattern( str )
	return string.format(
		"[%s%s]%s",
		mw.ustring.upper( mw.ustring.sub( str, 1, 1 ) ),
		mw.ustring.lower( mw.ustring.sub( str, 1, 1 ) ),
		mw.ustring.gsub( str, '^.', '' )
	)
end

local function checkForErrors( sublist )
	return mw.ustring.find( sublist, 'class="error' ) ~= nil
		-- Templates that should not be there
		or mw.ustring.find( sublist, 'id="disambig"' ) ~= nil -- {{disambig}}
		or mw.ustring.find( sublist, 'class="hatnote' ) ~= nil -- {{hatnote}}
		or mw.ustring.find( sublist, ' ambox ambox-' ) ~= nil -- {{ambox}}
		or mw.ustring.find( sublist, '__TOC__' ) ~= nil -- __TOC__
		or mw.ustring.find( sublist, 'UNIQ--references' ) ~= nil -- {{references}}
		-- Incorrect list markup
		or mw.ustring.find( sublist, '^%s*%*' ) ~= 1 -- does not start with a list
		or mw.ustring.find( sublist, '^%s*%*%*' ) ~= nil
		or mw.ustring.find( sublist, '\n%:%*' ) ~= nil
		-- Other incorrect markup
		or mw.ustring.find( sublist, '\n;%s*[А-ЯA-Z]' ) ~= nil -- incorrect bold text markup
		or mw.ustring.find( sublist, '\n=[^=]' ) ~= nil -- 1st level heading
		or mw.ustring.find( sublist, '\n==[^=]' ) ~= nil -- 2nd level heading
		or mw.ustring.find( sublist, '\n%-%-%-%-' ) ~= nil -- contains ---- in code
		or mw.ustring.find( sublist, '\n<div class="ts%-NL">' ) ~= nil -- {{NL}} after a line break
end

local function getEditLink( title, useRedirect )
	local mwTitle = mw.title.new( title )
	
	-- Handle redirects here because it affects action=edit link
	if useRedirect ~= true and mwTitle.redirectTarget ~= false then
		mwTitle = mwTitle.redirectTarget
	end
	
	local span = mw.html.create( 'span' )
		:addClass( 'ts-NL-edit mw-editsection-like plainlinks noprint navigation-not-searchable group-user-show' )
		:wikitext( string.format(
			'<span class="mw-editsection-bracket">[</span>[%s %s]<span class="mw-editsection-bracket">]</span>',
			mwTitle:fullUrl( 'action=edit' ),
			editLinkText
		) )
	
	return tostring( span )
end

local function getError( title, msg, setCategory )
	if setCategory == nil then
		setCategory = true
	end
	
	local errorText = msg
	if title ~= nil and title ~= '' then
		errorText = string.format( msg, title )
	end
	
	addWarning( nil, errorText )
	
	return '<div class="error">' .. errorText .. '</div>'
		.. ( setCategory and string.format( '[[Category:%s]]', errorCat ) or '' )
end

-- Prevent template loop by replacing template calls to the calls to this module
local function replaceSubtemplates( content )
	if isEmpty( content ) then
		return content
	end
	
	content = mw.ustring.gsub( content, '{{' .. templatePattern( templatePageName ) .. '/?|', '{{NL|' )
	content = mw.ustring.gsub( content, '{{' .. templatePattern( 'NL' ) .. '[23]?%|', moduleInvocationCode )
	
	return content
end

--[[
	@param {String} wikitext: Wikitext of just the list (i.e. each line is a list item)
	@param {String} symbol:   Special character used in the wikitext markup for the list, e.g. '*' or '#'
	@param {String} outerTag: Text portion of the tag for each list or sublist, e.g. 'ul' or 'ol'
	@param {String} innerTag: Text portion of the tag for each list item, e.g. 'li'
]]
local wikitextToHtmlList = function( wikitext, symbol, outerTag, innerTag )
	local listParts = {}
	for level, item in mw.ustring.gmatch( '\n' .. wikitext .. '\n', '\n(%' .. symbol .. '+)(.-)%f[\n]' ) do
	    table.insert( listParts, { level=level, item=item } )
	end
	table.insert( listParts, { level='', item='' } )
	
	local htmlList = {}
	for i, this in ipairs( listParts ) do
		local isFirstItem = ( i == 1 )
		local isLastItem = ( i == #listParts )
	    local lastLevel = isFirstItem and '' or listParts[ i - 1 ][ 'level' ]
	    local tags
	    if #lastLevel == #this.level then
	    	tags = '</'..innerTag..'><'..innerTag..'>'
	    elseif #this.level > #lastLevel then
	    	tags = string.rep( '<'..outerTag..'><'..innerTag..'>', #this.level - #lastLevel )
	    elseif isLastItem then
	    	tags = string.rep( '</'..innerTag..'></'..outerTag..'>', #lastLevel )
	    else -- ( #this.level < #lastLevel ) and not last item
	    	tags = string.rep( '</'..innerTag..'></'..outerTag..'>', #lastLevel - #this.level ) .. '</'..innerTag..'><'..innerTag..'>'
	    end
	    table.insert( htmlList, tags .. this.item )
	end
	return table.concat( htmlList )
end


--[[
	@param {String} wikitext: Wikitext excertp containg zero or more lists
	@param {String} symbol:   Special character used in the wikitext markup for the list, e.g. '*' or '#'
	@param {String} outerTag: Text portion of the tag for each list or sublist, e.g. 'ul' or 'ol'
	@param {String} innerTag: Text portion of the tag for each list item, e.g. 'li'
]]
local gsubWikitextLists = function( wikitext, symbol, outerTag, innerTag )
	-- temporarily remove list linebreaks... 
	wikitext = mw.ustring.gsub( wikitext .. '\n', '\n%' .. symbol, '¿¿¿' .. symbol )
	-- ...so we can grab the whole list (and just the list)...
	return mw.ustring.gsub(
		wikitext,
		'¿¿¿%'..symbol..'[^\n]+', 
		function( listWikitext )
			-- ...and then reinstate linebreaks...
			listWikitext = mw.ustring.gsub( listWikitext, '¿¿¿%' .. symbol, '\n' .. symbol )
			-- ...and finally do the conversion
			return wikitextToHtmlList( listWikitext, symbol, outerTag, innerTag )
		end
	)
end

-- Protects the templates from substitution by substituting them with their own parameters
function p._substing( frame )
	local args = getArgs( frame, {
		parentOnly = true,
	} )
	local mTemplateInvocation = require( 'Module:Template invocation' )
	local name = mTemplateInvocation.name( frame:getParent():getTitle() )
	
	if substWithRedirects then
		name = mw.ustring.gsub( name, 'Вложенный список/?', 'NL' )
	end
	
	return mTemplateInvocation.invocation( name, args )
end


function p.main( frame )
	if mw.isSubsting() then
		return p._substing( frame )
	end
	
	local args = getArgs( frame )
	local title = args[ 1 ]
	local displayedTitle = args[ 2 ] or ''
	
	local colon = ':'
	local appendedText = args[ 3 ] or ''
	if not isEmpty( appendedText ) then
		colon = ''
		appendedText = ' ' .. appendedText .. ':'
	end
	
	local currentTitle = mw.title.getCurrentTitle()
	if isEmpty( title ) then
		return getError( nil, noTitle, currentTitle.namespace == 0 )
	end
	
	-- Render a disambig page name without its bracketed suffix
	if displayedTitle == '' and mw.ustring.find( title, disambigPageSuffix, 1, true ) ~= nil then
		displayedTitle = mw.ustring.gsub( title, '^%s*(.+)%s+%b()%s*$', '%1' )
	end
	
	if displayedTitle ~= '' then
		displayedTitle = '|' .. displayedTitle
	end
	
	local intro = string.format(
		'<span class="dabhide">[[%s%s]]%s</span>%s',
		title,
		displayedTitle,
		colon,
		appendedText
	)
	
	-- frame:expandTemplate is used because mw.title:getContent() does not handle redirects
	local titleObj = mw.title.new( title )
	
	-- Invalid title
	if titleObj == nil then
		return getError( title, invalidTitle )
	end
	
	if titleObj.exists and titleObj.isRedirect then
		titleObj = titleObj.redirectTarget
	end
	
	-- The page does not exist, equal to current or returns empty
	if titleObj.exists ~= true or mw.title.equals( titleObj, currentTitle ) then
		return intro .. getEditLink( title, true ) .. getError( title, noContent )
	end
	
	local content = titleObj:getContent()
	content = replaceSubtemplates( content )
	local sublist = frame:preprocess( content )
	
	if mw.text.trim( sublist ) == '' then
		return intro .. getEditLink( title, true ) .. getError( title, noContent )
	end
	
	-- Reject transclusions that look like articles
	if mw.ustring.find( sublist, ' class="infobox' ) ~= nil
		or mw.ustring.find( sublist, ' class="navbox' ) ~= nil then
		return intro .. getEditLink( title, true ) .. getError( title, notDisambig )
	end

	intro = intro .. getEditLink( title )
	
	-- Remove 3rd+ level subheadings for easier section insertion (before error checks)
	sublist = mw.ustring.gsub( sublist, '\n(===+)(.-)%1\n-', '' )
	
	-- Check sublist for wikitext markup errors
	local hasErrors = checkForErrors( sublist )
	
	-- Test if the included page has links
	local hasInvocation, invocationPos = mw.ustring.find( content, moduleInvocationCode )
	local hasLinks = mw.ustring.find( content, '%*%s*\'*["«„]?\'*%[%[' )
		-- Two module invocations mean two different links
		or ( hasInvocation and mw.ustring.find( content, moduleInvocationCode, invocationPos ) )
		-- [[Модуль:Не переведено]]
		or mw.ustring.find( sublist, ' версия статьи «' )
		or mw.ustring.find( sublist, 'title="Элемент статьи «' )
	
	-- Replace list markers with HTML list openers
	sublist = gsubWikitextLists( '\n' .. sublist, '*', 'ul', 'li' )
	sublist = gsubWikitextLists( '\n' .. sublist, '#', 'ol', 'li' )
	
	-- Remove the bold text around links automatically
	sublist = mw.ustring.gsub( sublist, "<li>%s*'''%s*%[%[([^%]]+)%]%]%s*'''", '<li>[[%1]]' )
	
	-- Trim and replace double line breaks to avoid breaking the list
	sublist = mw.text.trim( sublist )
	sublist = mw.ustring.gsub( sublist, '\n\n', '<p>' )
	
	-- Merge adjacent lists
	sublist = mw.ustring.gsub( sublist, '</ul>\n-<ul>', '' )
	
	-- Replace remaining
	sublist = mw.ustring.gsub( sublist, '\n', '<br>' )
	
	-- Set categories for errors
	if linklessCat and not hasLinks then
		sublist = sublist .. string.format( '[[Category:%s]]', linklessCat )
		addWarning( title, noLinks )
	end
	
	if hasErrors then
		sublist = sublist .. string.format( '[[Category:%s]]', errorCat )
		addWarning( title )
	end
	
	return '<div class="ts-NL">' .. intro .. sublist .. '</div>'
end

return p