Dokumentacja modułu [zobacz] [edytuj] [historia] [odśwież]

Ten moduł zapewnia platformę programistyczną dla tworzenia szablonów budujących przypadki testowe szablonów Wikipedii. Przypadki testowe mogą być tworzone ręcznie, aczkolwiek te tworzone za pomocą szablonów opartych na module Lua, takim jak niniejszy moduł, mają taką zaletę, że argumenty szablonu muszą być wprowadzone jedynie jednokrotnie, co zmniejsza nakład pracy konieczny do tworzenia testów, a także zmniejsza ryzyko występowania błędów w danych wejściowych.

Porównaj edytuj

Ta funkcja jest obecnie wykorzystywana przez szablon {{Przypadek testowy}}.

Parametry edytuj

Funkcja do wykrywania wywołań szablonów z nierozpoznanymi, pustymi lub przestarzałymi parametrami. Należy ją wywołać ze wszystkimi możliwymi parametrami jakie przyjmuje szablon podlegający sprawdzaniu.

Opis parametrów
pole status uwagi
bez nazwy konfiguracja działania wymagany klasa lub klasa odstęp suffix
inny deklaracja pola obsługiwanego w szablonie opcjonalny txt, num, num?, uri, uri?, old, ^…$ lub pusty
Konfiguracja
  • klasa – ciąg znaków bez spacji umieszczany jako wartość atrybutu class w tagu <span> z wygenerowanym raportem
  • odstęp – ciąg znaków odstępu oddzielający pozostałą treść konfiguracji, jeśli zawiera znak nowej linii to przed wygenerowanym raportem jest również umieszczany znak nowej linii
  • suffix – treść dołączana po wygenerowanym raporcie, zwykle deklaracja technicznej kategorii
Sprawdzanie parametrów
  • pusty – parametr jest opcjonalny, jest to najczęściej stosowany sposób użycia
  • txt – oczekiwana jest dowolna niepusta wartość pola
  • text – oczekiwana jest dowolna niepusta wartość pola, która nie generuje obrazka
  • text? – oczekiwana jest wartość pola, która nie generuje obrazka
  • num – sprawdzanie czy przekazana wartość jest liczbą
  • num? – sprawdzanie czy przekazana wartość jest pusta lub jest liczbą
  • uri – sprawdzanie czy przekazana wartość jest linkiem obsługiwanym przez MediaWiki
  • uri? – sprawdzanie czy przekazana wartość jest pusta lub jest linkiem
  • grafika – sprawdzanie czy przekazana wartość jest nazwą pliku graficznego
  • grafika? – sprawdzanie czy przekazana wartość jest pusta lub jest nazwą pliku graficznego
  • old – informuje, że przekazywany parametr jest przestarzały
  • ^…$ – tekst rozpoczynający się znakiem ^ i zakończony na $ w całości traktowany jest jako wyrażenie regularne Lua wskazującego prawidłowy parametr
Tabela poprawności dla num i ^...$[a]
Modyfikator Brak parametru

np. {{formatprice}}

Pusty parametr

np. {{formatprice|1=}}

Niepusty parametr

np. {{formatprice|123}}

?[b] poprawne poprawne wg testu
bez mod. poprawne wg testu wg testu
! Brakujące pola: ... wg testu wg testu
Przykład
{{#invoke:Sprawdź|Parametry|=problemy-w-szablonie [[Kategoria:Błędy wywołań w szablonie]]|parametr_w_szablonie_1=|parametr_w_szablonie_2=num?}}

odn edytuj

Funkcja sprawdzająca prawidłowość parametrów w {{odn}}.

Uwagi edytuj

  1. Sprawdzone tylko dla testów num i ^...$. Pozostałe testy mogą nie mieć wszystkich modyfikatorów albo mogą zachowywać się inaczej.
  2. ^...$? nie jest obsługiwane, użyto {{#if:{{{1|}}}|^...$}}
local function checkUri(uri)
	local urilen = #uri
	for _,v in ipairs(mw.loadData("Moduł:Cytuj/dane").supportedUriSchemas) do
		if (#v < urilen) and (string.lower(string.sub(uri,1, #v)) == v) then
			return not string.match(uri, '%s')
		end
	end
end

function checkImageName(name)
	if not name or (#name==0) or string.match(name, "[#<>%[%]|{}]") then
		return false
	end
	
	local title = mw.title.makeTitle("Plik", name)
	if not title then
		return false
	end
	
	local res = {
		prefix = {
			plik = true,
			image = true,
			grafika = true,
			file = true,
		},
		extension = {
			jpg = true,
			jpeg = true,
			jpe = true,
			png = true,
			svg = true,
			tif = true,
			tiff = true,
			gif = true,
			xcf = true,
			pdf = true,
			djvu = true,
			webp = true,
		},
	}
	
	local prefix = string.match(name, "^:? *([^:]+) *:")
	if prefix and res.prefix[string.lower(prefix)] then
		return false
	end
	
	local extension = string.match(name, "%S.*%.([^%.]+)$")
	return extension and res.extension[string.lower(extension)]
end

local function findPlainHttp(text)
	if text then
		text = mw.ustring.gsub(text, "%[[hH][tT][tT][pP][sS]?://%S+", "_")
		return string.match(text, "[hH][tT][tT][pP][sS]?://%S")
	end
end

local function findFile(text)
	-- schowaj wyjątek: obrazek generowany przez Szablon:Link-interwiki
	text = mw.ustring.gsub(text, "%[%[Plik:Wikidata%-logo%-en%.svg|10x9px|link=:d:Q%d+|Informacje powiązane z artykułem „.-” w Wikidanych%]%]", "")
	-- normalize
	text = mw.ustring.gsub(text, "%s*[Pp]lik%s*:%s*", "Plik:")
	text = mw.ustring.gsub(text, "%s*[Ff]ile%s*:%s*", "Plik:")
	text = mw.ustring.gsub(text, "%s*[Gg]rafika%s*:%s*", "Plik:")
	text = mw.ustring.gsub(text, "%s*[Ii]mage%s*:%s*", "Plik:")
	return mw.ustring.match(text, "%[%[Plik:([^%[%]|]+)[|%]]")
end

-- Lista of template params for wikicode. Like this:
-- | grafika = | data śmierci = | www = 
local function listToTplParams(strArray)
	return "| " .. table.concat(strArray, " = | ") .. " =";
end

local function showTemplate(templateName, args)
	local result = {}
	local flags = {}
	table.insert(result, mw.text.nowiki("{{"))
	if mw.isSubsting() then
		table.insert(result, "subst:")
	end
	
	local title = mw.title.new(templateName)
	if not args then
		table.insert(result, "[[")
		table.insert(result, title.nsText)
		table.insert(result, ":")
		table.insert(result, title.text)
		table.insert(result, "|")
	end

	if title.namespace ~= 10 then
		table.insert(result, title.nsText)
		table.insert(result, ":")
	end

	table.insert(result, title.text)
	if not args then
		table.insert(result, "]]")
	end

	if args then
		local i = 1
		while args[i] do
			table.insert(result, "|")
			table.insert(result, mw.text.nowiki(args[i]))
			i = i + 1
		end
		
		for k, v in pairs(args) do
			local index = tonumber(k)
			if (type(k) == "string") or (index and ((index < 1) or (index > i))) then
				table.insert(result, "|")
				table.insert(result, tostring(k))
				table.insert(result, "=")
				table.insert(result, mw.text.nowiki(v))
			end
		end
	end

	table.insert(result, mw.text.nowiki("}}"))
	return table.concat(result)
end

local function infoboxCatTitle(infobox, class)
	local catTitle = mw.title.makeTitle(14, "Infoboksy – "..class.." – "..infobox)
	if not catTitle.exists then
		mw.logObject(catTitle, "Dedykowana kategoria błędów w infoboksie nie istnieje")
		catTitle = mw.title.makeTitle(14, "Infoboksy – "..class)
	end
	
	mw.logObject(catTitle, "infoboxCatTitle")
	return catTitle
end

local function daysInMonth(year, month)
	if month == 1 then return 31 end
	if month == 2 then return ((year % 4) == 0) and 29 or 28 end
	if month == 3 then return 31 end
	if month == 4 then return 30 end
	if month == 5 then return 31 end
	if month == 6 then return 30 end
	if month == 7 then return 31 end
	if month == 8 then return 31 end
	if month == 9 then return 30 end
	if month == 10 then return 31 end
	if month == 11 then return 30 end
	if month == 12 then return 31 end
	return 0
end

local function isDate20YYMMDD(date)
	local y, m, d = mw.ustring.match(date, "^(20[0-9][0-9])%-([01][0-9])%-([0123][0-9])$")
	local year = tonumber(y)
	local month = tonumber(m)
	local day = tonumber(d)
	local result = year and month and day
		and (year >= 2001)
		and (month >= 1) and (month <= 12)
		and (day >= 1) and (day <= daysInMonth(year, month))
	if not result then
		mw.logObject({date=date,year=year,month=month,day=day}, "isDate20YYMMDD")
	end
	return result
end

return {
	["Porównaj"] = function(frame)
		local config = frame:getParent().args[""] or ""
		local options = mw.text.split(config, "|")
		local templateName = mw.text.trim(options[1])
		if #templateName == 0 then
			local title = mw.title.getCurrentTitle()
			if title.namespace == 10 then
				templateName = mw.ustring.match(title.text, "^(.-)/opis")
					or mw.ustring.match(title.text, "^(.-)/test")
					or mw.ustring.match(title.text, "^(.-)/brudnopis")
					or title.text
			end
			if #templateName == 0 then
				mw.log("brak nazwy szablonu")
				return
			end
		end
	
		local templateTitle = mw.title.new(templateName, 10)
		if templateTitle.id == 0 then
			mw.log("szablon '"..templateName.."' nie istnieje")
			return
		end
		
		local sandboxName = templateName.."/brudnopis"
		local sandboxTitle = mw.title.new(sandboxName, 10)
		if sandboxTitle.id == 0 then
			mw.log("brudnopis '"..sandboxName.."' nie istnieje")
			return
		end
		
		local i = 2
		local showparams = true
		local showinfo = true
		local vertical = false
		while i <= #options do
			local option = mw.text.trim(options[i])
			if option == "bez wikikodu" then
				showparams = false
			elseif option == "bez opisu" then
				showinfo = false
			elseif option == "pionowo" then
				vertical = true
			end
			
			i = i + 1
		end
	
		local templateParams = {}
		local params = {}
		for k, v in pairs(frame:getParent().args) do
			if k ~= "" then
				templateParams[k] = v
				table.insert(params, k)
			end
		end
	
		local result = {}
		table.insert(result, '<table style="width: 100%;">')
		
		if showparams and (#params > 0) then
			local compare = function(a, b)
				-- return a < b
				if (type(a) == "number") and (type(b) == "number") then
					return a < b
				end
				
				if (type(a) == "string") and (type(b) == "string") then
					return a < b
				end
				
				if (type(a) == "number") and (type(b) == "string") then
					return true
				end
				
				return false
			end
			
			table.sort(params, compare)
			table.insert(result, "<caption><code>&#x7B;&#x7B;")
			table.insert(result, templateName)
			for i, k in ipairs(params) do
				table.insert(result, " &#x7C; ")
				local p = mw.text.nowiki(tostring(k))
				local v = mw.text.nowiki(templateParams[k])
				table.insert(result, p)
				table.insert(result, " = ")
				table.insert(result, v)
			end
			table.insert(result, "&#x7D;&#x7D;</code></caption>")
		end
		
		local templateResult = frame:expandTemplate{ title=templateName, args=templateParams}
		local sandboxResult = frame:expandTemplate{ title=sandboxName, args=templateParams}
		
		if templateResult and string.match(templateResult, "^{|") then
			templateResult = "\n"..templateResult
		end
		if sandboxResult and string.match(sandboxResult, "^{|") then
			sandboxResult = "\n"..sandboxResult
		end
		
		if vertical and showinfo then
			table.insert(result, '<tr><th style="width: 15em">[[Szablon:')
			table.insert(result, templateName)
			table.insert(result, '|Szablon]]</th><td>')
			table.insert(result, templateResult)
			table.insert(result, '</td></tr><tr><th>[[Szablon:')
			table.insert(result, sandboxName)
			table.insert(result, '|Brudnopis szablonu]]</th><td>')
			table.insert(result, sandboxResult)
			table.insert(result, '</td></tr>')
		elseif vertical then
			table.insert(result, '<tr><td>')
			table.insert(result, templateResult)
			table.insert(result, '</td></tr><tr><td>')
			table.insert(result, sandboxResult)
			table.insert(result, '</td></tr>')
		else
			if showinfo then
				table.insert(result, '<tr><th style="width: 50%;">[[Szablon:')
				table.insert(result, templateName)
				table.insert(result, '|Szablon]]</th><th style="width: 50%;">[[Szablon:')
				table.insert(result, sandboxName)
				table.insert(result, '|Brudnopis szablonu]]</th></tr>')
			end
			
			table.insert(result, '<tr style="vertical-align: top;"><td>')
			table.insert(result, templateResult)
			table.insert(result, '</td><td>')
			table.insert(result, sandboxResult)
			table.insert(result, '</td></tr>')
		end
		
		table.insert(result, "</table>")
		return table.concat(result)
	end,

	["Parametry"] = function(frame)
		if mw.title.getCurrentTitle().contentModel ~= "wikitext" then
			mw.logObject(mw.title.getCurrentTitle().contentModel, "mw.title.getCurrentTitle().contentModel")
			return -- to nie ma sensu w takim wypadku
		end

		local unknown = {}
		local invalid = {}
		local deprecated = {}
		local nakedurl = {}
		local files = {}
		local templateName = frame:getParent():getTitle()
		local infobox = mw.ustring.match(templateName, "^Szablon:(.- infobox)$")
		local config = frame.args[""]
		local class, space, category = string.match(config or "", "^%s*(%S+)(%s+)(.-)%s*$")
		local nl = space and string.match(space, "\n") or ""
		class = class or config

		local function argName(arg)
			return type(arg) ~= "string" and tostring(arg) or ('"'..arg..'"')
		end
		
		local required = {}
		for k, v in pairs(frame.args) do
			if mw.ustring.match(v, "!$") then
				required[k] = true
				if infobox then
					-- odróżniaj pola sugerowane w infoboksach, które mogą być puste
					if v == "!" or mw.ustring.match(v, "%?!$") then
						required[k] = false
					else
						local pattern = string.match(v,"^(^.-$)!?$")
						if pattern and mw.ustring.match("", pattern) then
							required[k] = false
						end
					end
				end
			end
		end
		
		local emptyArg = false
		for k, v in pairs(frame:getParent().args) do
			required[k] = nil
			local kind = frame.args[k]
			if kind == "" or kind == "!" then
				kind = "text?"
			end
				
			if k == "" then
				emptyArg = v
			elseif not kind then
				table.insert(unknown, argName(k))
			elseif (kind == "num") or (kind == "num!") then
				local n = tonumber(v)
				if not n then table.insert(invalid, argName(k)) end
			elseif (kind == "num?") or (kind == "num?!") then
				local n = (#v == 0) or tonumber(v)
				if not n then table.insert(invalid, argName(k)) end
			elseif (kind == "grafika") or (kind == "grafika!") then
				if findPlainHttp(v) then
					table.insert(nakedurl, argName(k))
				else
					local g = checkImageName(v)
					if not g then table.insert(invalid, argName(k)) end
				end
			elseif (kind == "grafika?") or (kind == "grafika?!") then
				if findPlainHttp(v) then
					table.insert(nakedurl, argName(k))
				else
					local g = (#v == 0) or checkImageName(v)
					if not g then table.insert(invalid, argName(k)) end
				end
			elseif (kind == "uri") or (kind == "uri!") then
				local u = checkUri(v)
				if not u then table.insert(invalid, argName(k)) end
			elseif (kind == "uri?") or (kind == "uri?!") then
				local u = (#v == 0) or checkUri(v)
				if not u then table.insert(invalid, argName(k)) end
			elseif (kind == "txt") or (kind == "txt!") then
				if #v == 0 then
					table.insert(invalid, argName(k))
				elseif checkUri(v) then
					table.insert(nakedurl, argName(k))
				elseif findPlainHttp(v) then
					table.insert(nakedurl, argName(k))
				end
			elseif (kind == "text") or (kind == "text!") or (kind == "text?") then
				if ((kind ~= "text?") and (#v == 0)) then
					table.insert(invalid, argName(k))
				elseif findFile(v) then
					table.insert(files, argName(k))
				elseif checkUri(v) then
					table.insert(nakedurl, argName(k))
				elseif findPlainHttp(v) then
					table.insert(nakedurl, argName(k))
				end
			elseif kind == "old" then
				table.insert(deprecated, argName(k))
			elseif kind == "uri*" then -- specjalny przpadek dla pola 'url' w [[Szablon:Cytuj]]
				local u = checkUri(v) or checkUri(mw.text.unstripNoWiki(v))
				if not u then table.insert(invalid, argName(k)) end
			else
				local pattern = string.match(kind,"^(^.-$)!?$")
				if pattern and not mw.ustring.match(v, pattern) then
					table.insert(invalid, argName(k))
				elseif (#v > 0) and checkUri(v) then
					table.insert(nakedurl, argName(k))
				elseif findPlainHttp(v) then
					table.insert(nakedurl, argName(k))
				end
			end
		end
		
		local missing = {}
		local suggested = {}
		mw.logObject(required, "required")
		for k, v in pairs(required) do
			table.insert(v and missing or suggested, k)
		end
		
		if (#missing == 0) and (#suggested == 0) and (#unknown == 0) and (#invalid == 0) and (#deprecated == 0) and (#nakedurl == 0) and (#files == 0) then
			return nil
		end

		local errorClasses = 0
		
		-- generate messages for each category of problems
		local messages = {}
		if #invalid > 0 then
			errorClasses = errorClasses + 2
			table.insert(messages, "Nieprawidłowe/puste pola: " .. mw.text.listToText(invalid) .. ".")
		end

		if (#missing > 0) or (#suggested > 0) then
			errorClasses = errorClasses + 1
			if (#missing > 0) then table.insert(messages, "Brakujące pola: " .. mw.text.listToText(missing) .. ".") end
			if (#suggested > 0) then table.insert(messages, "Sugerowane pola (wstaw w kodzie): <code>" .. listToTplParams(suggested) .. "</code>") end
		end

		if #unknown > 0 then
			errorClasses = errorClasses + 4
			table.insert(messages, "Nieznane pola: " .. mw.text.listToText(unknown) .. ".")
		end

		if #deprecated > 0 then
			errorClasses = errorClasses + 8
			table.insert(messages, "Przestarzałe pola: " .. mw.text.listToText(deprecated) .. ".")
		end
		
		if #nakedurl > 0 then
			errorClasses = errorClasses + 16
			table.insert(messages, "Gołe linki: " .. mw.text.listToText(nakedurl) .. ".")
		end
		
		if #files > 0 then
			errorClasses = errorClasses + 32
			table.insert(messages, "Nieoczekiwana grafika: " .. mw.text.listToText(files) .. ".")
		end

		-- render messages
		local message = mw.html.create()
		if emptyArg then
			message:wikitext("|=", emptyArg, "| ")
		end
		if infobox then
			message:wikitext("\n* ", table.concat(messages, "\n* "))
		else
			message:wikitext(table.concat(messages, " "))
		end
		message = tostring(message)

		-- result container
		local result = mw.html.create(infobox and "table" or "span")
		result:addClass("problemy")
			:addClass(class or nil)
			:attr("aria-hidden", "true")

		local restext = result
		if infobox then
			result:addClass("infobox")
			restext = result:tag("tr"):tag("td")
		else
			result:attr("data-nosnippet", "")
		end
		
 		if templateName then
			restext:wikitext(showTemplate(templateName).." ")
			-- pomiń ten komunikat jeśli zabraknie tylko pól sugerowanych w infoboksie
			if not infobox or (errorClasses ~= 1) or (#missing > 0) then
	 			local warning = mw.html.create()
	 			warning:tag("code"):wikitext(showTemplate(templateName, frame:getParent().args))
	 			warning:wikitext(" ")
	 			warning:tag("span"):addClass("problemy"):wikitext(message)
	 			mw.addWarning(tostring(warning))
 			end
 		end
 		
 		restext:wikitext(message)
 
		if category then
			result:wikitext(category)
		end
		if infobox then
			if (errorClasses == 1) and (#missing <= 0) then
				result:addClass("tylko-braki") -- tylko SUGEROWANE braki
			end
			if mw.title.getCurrentTitle().namespace == 0 then
				if (#missing > 0) or (#suggested > 0) then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "brakujące parametry").text, "]]")
				end
				if #invalid > 0 then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "nieprawidłowe parametry").text, "]]")
				end
				if #unknown > 0 then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "nieznane parametry").text, "]]")
				end
				if #deprecated > 0 then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "przestarzałe parametry").text, "]]")
				end
				if #nakedurl > 0 then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "gołe linki").text, "]]")
				end
				if #files > 0 then
					result:wikitext("[[Kategoria:", infoboxCatTitle(infobox, "grafika w nieodpowiednim miejscu").text, "]]")
				end
			end
		end

		return nl..tostring(result)
	end,

	["wartość"] = function(frame)
		local msg = frame.args.msg
		local value = tonumber(frame.args[1]) or frame.args[1]
		local index = 2 -- pierwszy wzór
		while value do
			local pattern = frame.args[index]
			if not pattern then
				break -- nie ma więcej wzorów
			end
			
			if mw.ustring.match(value, "^"..pattern.."$") then
				return -- wzór pasuje, nie ma błędów
			end
			
			-- następny wzór
			index = index + 1
		end
		
		-- komunikat błędu
		return msg
	end,

	["odn"] = function(frame)
		local pf = frame:getParent()
		local i = 0 -- liczba argumentów pozycyjnych
		local problems = false
		local yeardetected = false
		while true do
			local arg = pf.args[i + 1]
			if not arg then
				problems = i == 0 and "brak argumentów" or false
				break
			end
	 
			if (i >= 5) or yeardetected then
				problems = "za dużo argumentów pozycyjnych"
				break
			end
	 
			if #arg == 0 then
				problems = "pusty argument"
				break
			end
	 
			if arg ~= mw.text.trim(arg) then
				problems = "nieoczekiwane odstępy na początku lub końcu argumentu"
				break
			end
	 
			if string.match(arg, "^%d+%l?$") then
				yeardetected = true
				if i == 0 then
					problems = "rok musi być ostatnim parametrem po nazwiskach autorów"
					break
				end
			elseif string.match(arg, "^s[%-%.:]%s*%d+") then
				problems = "prawdopodobnie nieprawidłowo podany numer strony"
				break
			elseif string.match(arg, "%s%s") then
				problems = "podwójne odstępy"
				break
			elseif mw.ustring.match(arg, "^%a+%d") then
				if not mw.ustring.match(arg, "^[%u%d]+$") then
					problems = "prawdopodobnie sklejone argumenty (brak pionowej kreski)"
					break
				end
			elseif mw.ustring.match(arg, "^OdeB ") then
				-- [[Ordre de Bataille]]
			elseif mw.ustring.match(arg, "^%u%l+%u") then
				local justification = {
					["De"] = true,
					["Del"] = true,
					["Di"] = true,
					["Le"] = true,
					["Mac"] = true,
					["Mc"] = true,
					["Te"] = true, -- TeSelle
					["Sar"] = true, -- SarDesai
					["Van"] = true, -- VanBuren
					["La"] = true, -- LaSalle
				}
				if not justification[mw.ustring.match(arg, "^%u%l+")] then
					problems = "prawdopodobnie sklejone argumenty (brak pionowej kreski)"
					break
				end
			end
	 
			i = i + 1
		end
	 
		if not problems then
			local odn = pf.args.odn
			if odn and ((#odn ~= 1) or (odn < "a") or (odn > "z")) then
				problems = "nieoczekiwany parametr odn"
			end
		end
	 
	 	if not problems then
	 		local s = pf.args.s
	 		if s and string.match(s, "&[a-z]+;") then
	 			problems = "użyto encji HTML w numerze strony"
	 		end
	 	end
	 
		if not problems then
			if pf.args.strona or pf.args.ss or pf.args.strony or pf.args.p or pf.args.page or pf.args.pp or pf.args.pages then
				problems = "przestarzały parametr z numerem strony"
			end
		end
	
		local category = "Szablon odn do sprawdzenia"
	 	if not problems then
	 		
	 		local function TextLinkToRFC()
	 			return (i == 1) and (pf.args.ref == "nie") and mw.ustring.match(pf.args[1], "^RFC [1-9][0-9]*$")
	 		end
	 		
	 		local s = pf.args.s or ""
	 		local l = pf.args.loc or ""
	 		if (#s == 0) and (#l == 0) then
	 			local exception = TextLinkToRFC()
	 			if not exception then
	 				category = "Szablon odn bez numeru strony"
	 				problems = "brak numeru strony"
	 			end
	 		end
	 	end
	 
	 	if not problems then
	 		return nil
	 	end
	 	
	 	local result = mw.html.create("span")
	 		:addClass("problemy")
	 		:addClass("problemy-w-odn")
			:attr("aria-hidden", "true")
			:attr("data-nosnippet", "")
			:attr("data-problemy", "ODN: "..problems)
	 	if mw.title.getCurrentTitle().namespace == 0 then
	 		result:wikitext("[[Kategoria:", category, "]]")
	 	end
	 	result:wikitext("&#8201;") -- thin space
	 	return tostring(result)
	end,
	
	["Wikidane"] = function(frame)
		local property = frame.args.cecha
		local field = frame.args.pole
		local value = frame.args[1]
		if not property or not field then
			return
		end
		
		if not value then
			value = frame:getParent().args[field]
			if not value or (#value == 0) then
				return
			end
		end
		
		local entity = mw.wikibase.getEntity()
		if not entity or not entity.claims or not entity.claims[property] then
			return
		end
		
		for i, v in ipairs(entity.claims[property]) do
			if v.mainsnak.snaktype == "value" then
				if value == v.mainsnak.datavalue.value then
					return
				end
			end
		end
	
		local template = frame:getParent():getTitle()
		local infobox = mw.ustring.match(template, "^Szablon:(.- infobox)$")
		return mw.ustring.format("[[Kategoria:%s – niezgodność w Wikidanych – %s – %s]]", infobox and "Infoboksy" or "Szablony", infobox or template, field)
	end,

	["bez parametrów"] = function(frame)
		for k, v in pairs(frame:getParent().args) do
			return nil
		end
		
		return "tak"
	end,

	["pole z hostem"] = function (frame)
		local host = frame.args.host
		if host and (#host > 0) then
			for k, v in pairs(frame:getParent().args) do
				local link = string.match(v, "[hH][tT][tT][pP][sS]?://[%S]+")
				if link then
					local uri = mw.uri.new(link)
					local valid, _ = mw.uri.validate(uri)
					if valid and uri.host and (#uri.host > 0) then
						if host == uri.host then
							mw.logObject({k, link}, "cały")
							return k
						end
						
						if #host < #uri.host then
							local s1 = '.'..host
							local s2 = string.sub(uri.host, -#s1)
							if s1 == s2 then
								mw.logObject({k, link}, "fragment")
								return k
							end
						end
					end
				end
			end
		end
	end,
	
	["pola z autorami"] = function (frame)
		local result = {}
		local nazwisko = frame.args["nazwisko"]
		local imie = frame.args["imię"]
		local autor = frame.args["autor"]
		local link = frame.args["link"]
		local maxIndex = tonumber(frame.args["max"])
		local prefix = frame.args["przed"] or ""
		local suffix = frame.args["po"] or ""
		for i = 1, maxIndex do
			local s = i == 1 and "" or tostring(i)
			local nin = string.gsub(nazwisko, '#', s)
			local iin = string.gsub(imie, '#', s)
			local ain = string.gsub(autor, '#', s)
			local lin = string.gsub(link, '#', s)
			local niv = frame:getParent().args[nin]
			local iiv = frame:getParent().args[iin]
			local aiv = frame:getParent().args[ain]
			local liv = frame:getParent().args[lin]
			local nis = niv and (#niv > 0)
			local iis = iiv and (#iiv > 0)
			local ais = aiv and (#aiv > 0)
			local lis = liv and (#liv > 0)
			local bad = (nis and ais) -- nazwisko -> zbędny autor
				or (nis and not iis) -- nazwisko bez imienia
				or (lis and not nis and not ais) -- tylko link
				or (iis and not nis) -- imię bez nazwiska
			if bad then
				table.insert(result, i)
			end
		end
	
		if #result > 0 then
			return prefix..mw.text.listToText(result)..suffix
		end
	end,
	
	["uri"] = function(frame)
		mw.logObject(frame:getParent():getTitle(), "parent:title")
		_ = mw.title.new("Moduł:Sprawdź/deprecated/uri").id
		local link = frame.args["link"]
		local space = frame.args["spacja"]
		local check = checkUri(link)
		if check then
			return link
		end
		
		return (space and (check ~= nil)) and link or ""
	end,

	--[[Sprawdzanie url do szablonów
	
		Wykorzystanie:
		{{#invoke:Sprawdź|url|{{{www|}}}|[{{{www}}} Strona internetowa]}}
		{{#invoke:Sprawdź|url|{{{www}}}|[{{{www}}} Strona internetowa]}}
	
		@param #1 Url do sprawdzenia.
			Ciąg typu "{{{abc}}}" pominie sprawdzenie, żeby pokazać wartość na stronie szablonu.
		@param #2 Tekst do wyświetlenia gdy OK.
		@return pusty string gdy błędny, "ok" lub zawartość #2 gdy OK.
	]] 
	["url"] = function(frame)
		local link = frame.args[1]
		local okText = frame.args[2] or "ok"
		local isValid = string.find(link, '{{{') == 1 or checkUri(link)
		if isValid then
			return okText
		end
		return ""
	end,
	
	["lista nazw niepustych argumentów"] = function(frame)
		local argNames = {}
		for k, v in pairs(frame:getParent().args) do
			if #mw.text.trim(v) > 0 then
				table.insert(argNames, tostring(k))
			end
		end
		
		return table.concat(argNames, ", ")
	end,
	
	["zapis daty dostępu"] = function(frame)
		local accessDate = frame:getParent().args["data dostępu"]
		if accessDate and (#accessDate > 0) then
			if not isDate20YYMMDD(accessDate) then
				local builder = mw.html.create('span')
					:addClass('problemy')
					:wikitext('zły zapis daty dostępu')
				if mw.title.getCurrentTitle().namespace == 0 then
					builder:wikitext('[[Kategoria:Szablon cytowania – zły zapis daty dostępu]]')
				end
				return builder
			end
		end
	end,
}