Modul:Official links/sandkasse2

Fra Wikipedia, den frie encyklopedi
Moduldokumentasjon


require('Module:No globals')

local contLang = mw.language.getContentLanguage()
local prioritize = {}
prioritize = {
	['Q9043'] = true,	-- norsk
	['Q25167'] = true,	-- bokmål (målform av norsk)
	['Q25164'] = true,	-- nynorsk (målform av norsk)
	['Q9035'] = true,	-- dansk (nærstående språk)
	['Q9027']= true,	-- svensk (nærstående språk)
	['Q1860'] = true,	-- engelsk (vanlig å kunne dette språket)
	['Q7979'] = true,	-- britisk-engelsk (variant av engelsk)
	['Q7976'] = true,	-- amerikansk-engelsk (variant av engelsk)
						-- flere varianter av engelsk...
	['Q188'] = true,	-- tysk (vanlig å kunne dette språket)
	['Q150'] = true,	-- fransk (vanlig å kunne dette språket)	
	['Q33947'] = true,	-- nordsamisk (offisielt språk i norge)
	['Q13293'] = true,	-- sørsamisk (offisielt språk i norge)
	['Q56322'] = true,	-- lulesamisk (offisielt språk i norge)
	['Q165795'] = true,	-- kvensk (offisielt språk i norge)
	['Q36641'] = true,	-- romani (offisielt språk i norge)
	['Q13201'] = true,	-- romanes (offisielt språk i norge)
}

local domain = {}
domain = {
	['net'] = '«net-domene»',
	['org'] = '«org-domene»',
	['no'] = '«norsk domene»',
	['sv'] = '«svensk domene»',
	['dk'] = '«dansk domene»',
	['de'] = '«tysk domene»',
	['nl'] = '«nederlandsk domene»',
}

local messages = {}
messages = {
	['qualifier-default-empty'] = 'tom verdi (en feil)',
	['qualifier-P407-empty'] = 'tomt språk (en feil)',
	['qualifier-default-missing'] = 'mangler kvalifikator',
	['qualifier-default-missing-P407'] = 'ukjent språk',
	['qualifier-P856-missing'] = 'ukjent språk',
	['qualifier-P1019-missing'] = 'ukjent språk',
	['qualifier-P1581-missing'] = 'ukjent språk',
	['qualifier-P856-missing-P407'] = 'ukjent språk',
	['qualifier-P1019-missing-P407'] = 'ukjent språk',
	['qualifier-P1581-missing-P407'] = 'ukjent språk',
	['ext-link-with-lang-normal'] = '<span class="bjeller" style="color: gray;cursor:help;" title="$2"><small>($1)</small></span> [$3 $4]',
	['ext-link-with-lang-preferred'] = '<span class="bjeller" style="color: gray;cursor:help;" title="$2"><small>($1)</small></span> [$3 $4] (foretrukket)',
	['ext-link-normal'] = '[$1 $2]',
	['ext-link-preferred'] = '[$1 $2] (foretrukket)',
	['initial-P407-combiner'] = '/',
	['initial-P305-combiner'] = '/',
	['final-P407-combiner'] = '$1/$2',
	['initial-default-combiner'] = ', ',
	['final-default-combiner'] = '$1 og $2',
	['first-list-item'] = '$1: $2',
	['rest-list-item'] = '* $1: $2',
	['first-list-item-with-additional'] = '$1: $2 $3',
	['rest-list-item-with-additional'] = '* $1: $2 $3',
	['no-list-available'] = 'Ikke noe $1 tilgjengelig',
	['cat-inclusion'] = 'Artikler med $1',
	['cat-exclusion'] = 'Artikler uten $1',
	['no-links-available'] = 'Ingen automatiske lenker tilgjengelig',
	['wikidata-linkback-edit'] = 'Redigere på wikidata',
	['results-show-more'] = 'Vis flere',
	['results-hide-more'] = 'Skjul',
}

local function pri(code)
		return prioritize[code]
end

local function guess(uri)
	-- this is for country codes
	local _,_,code = uri:match('//([-%w]+)%.([-%w]+)%.(%w+)')
	if code and domain[code] then
		return domain[code]
	end
	local _,code = uri:match('//([-%w]+)%.(%w+)')
	if code and domain[code] then
		return domain[code]
	end
	return uri
end

local function shortenUri(uri)
	-- get the domain-part of the URI
	local domainpart = uri:match('//([^/]+)')
	local retval = nil
	if domainpart then
		-- split it up
		local splitTable = {}
		for i in string.gmatch(domainpart,"[^%.]+") do
			-- insert the tld, domain and subdomains in reverse order
			table.insert(splitTable, i)
		end
		-- now we should have a list of the subdomains, domain and tld.
		local t = {}
		if #splitTable>=1 then
			table.insert(t, 1, table.remove(splitTable))
		end
		if #splitTable>=1 then
			table.insert(t, 1, table.remove(splitTable))
		end
		if #splitTable>=1 then
			table.insert(t, 1, table.remove(splitTable))
		end
		if #splitTable>=1 then
			table.insert(t, 1, "")
		end
		-- the rest we don't care about..
		retval = table.concat(t, '.')
	end
	return retval
end

local function g(...)
	for _,v in ipairs(arg) do
		if messages[v] then
			return messages[v]
		end
	end
	return '<'..arg[1]..'>'
end

local labels = {}

local function buildList( pid, t  )
	if #t == 0 then
		return ''
	elseif #t == 1 then
		if t[1].langcode and #t[1].langcode then
			return t[1].text .. ' ' .. t[1].langcode
		else
			return t[1].text
		end
		
	end
	local tt = t
	if t[1].text then 
		for i,v in ipairs(t) do
			tt[i] = v.text
		end
	end
	local last = table.remove(tt)
	local str = table.concat(tt, mw.message.newRawMessage(g('initial-' .. pid .. '-combiner', 'initial-default-combiner')):plain())
	return mw.message.newRawMessage(g('final-' .. pid .. '-combiner', 'final-default-combiner'), str, last):plain()
end

-- todo: messy solution, refactor
local function wrap( content, marker )
	if content == '' or content == nil then
		return nil
	end
	return '<span class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..marker
		..'" data-expandtext="'..g('results-show-more')..'" data-collapsetext="'..g('results-hide-more')
		..'"><span class="mw-collapsible-content">' .. content .. '</span></span>'
end

local function getEntityLabel( id )
	local entity = mw.wikibase.getEntity( id )
	if entity then
		return entity:getLabel()
	end
end

local function findEntityLabel( id )
	if not labels[id] then
		local label = getEntityLabel( id )
		if label then
			labels[id] = label -- labels is an outer structure
		end
	end
	return labels[id]
end


local mainFormatter = {}
mainFormatter['string'] = function( pid, datavalue )
	if datavalue['type'] ~= 'string' then
		return nil
	end
	return datavalue.value
end

local qualLangCode = {}
qualLangCode['wikibase-entityid'] = function( pid, datavalue )
	if datavalue['type'] ~= 'wikibase-entityid' then
		return nil
	end
	if datavalue.value['entity-type'] ~= 'item' then
		return nil
	end
	local claim = {}
	local langs = {}
	local entity = mw.wikibase.getEntityObject('Q'..datavalue.value["numeric-id"])
	if entity and entity.claims then
		claim = entity.claims['P305'] -- IETF language tag
		-- traverse all snaks in this claim
		for _, claimsnak in ipairs( claim ) do
			if claimsnak ~=nil and claimsnak.mainsnak ~= nil and claimsnak.mainsnak.snaktype == "value" and claimsnak.mainsnak.datatype == "string" then
				langs[#langs+1] = claimsnak.mainsnak.datavalue.value
			end
		end
	else
		return nil
	end
	return table.concat(langs, g('initial-P305-combiner'))
end

local qualFormatter = {}
qualFormatter['wikibase-entityid'] = function( pid, datavalue )
	if datavalue['type'] ~= 'wikibase-entityid' then
		return nil
	end
	if datavalue.value['entity-type'] ~= 'item' then
		return nil
	end
	return findEntityLabel( 'Q'..datavalue.value["numeric-id"] )
end

local qualPrefer = {}
qualPrefer['wikibase-entityid'] = function( pid, datavalue )
	if datavalue['type'] ~= 'wikibase-entityid' then
		return nil
	end
	if datavalue.value['entity-type'] ~= 'item' then
		return nil
	end
	return pri( 'Q'..datavalue.value["numeric-id"] ) or false
end

local main = {}
main.P856 = {
	types = {
		snaktype = 'value',
		datatype = 'url',
	},
	formatter = mainFormatter['string']
}
main.P1019 = {
	types = {
		snaktype = 'value',
		datatype = 'url',
	},
	formatter = mainFormatter['string']
}
main.P1581 = {
	types = {
		snaktype = 'value',
		datatype = 'url',
	},
	formatter = mainFormatter['string']
}


local qual = {}
qual.P407 = {
	types = {
		snaktype = 'value',
		datatype = 'wikibase-item',
	},
	formatter = qualFormatter['wikibase-entityid'],
	langcode = qualLangCode['wikibase-entityid'],
	preferred = qualPrefer['wikibase-entityid']
}

local qorder = {'P407'}

local p = {}

function p.findMainLinks(pid, qid)
	local head = {}
	local tail = {}
	-- get the entity we are checking
	local entity = mw.wikibase.getEntityObject( qid )
	-- to avoid deep tests
	if not entity or not entity.claims then
		return head, tail
	end
	-- get the claims for this entity
	local statements = entity.claims[pid]
	-- to avoid deep tests
	if not statements then
		return head, tail
	end
	-- let's go through the claims
	for _, claim in ipairs( statements ) do
		-- to avoid deep tests
		if not claim then
			claim = {}
		end
		local valid = true
		if claim['type'] ~= 'statement' then
			valid = valid and false
		end
		if claim['rank'] == 'deprecated' then
			valid = valid and false
		end
		local mainsnak = claim.mainsnak or {}
		if not mainsnak or not main[pid] then
			valid = valid and false
		end
		if ((main[pid] and mainsnak.snaktype ~= main[pid].types.snaktype)
			or (main[pid] and mainsnak.datatype ~= main[pid].types.datatype))
		then
			valid = valid and false
		end
		if valid then
			local preferred = claim['rank'] == 'preferred'
			-- get the content of the claim (the URL)
			local mainStr = main[pid].formatter(pid, mainsnak.datavalue)
			local optionals = {}
			-- get any qualifiers for this claim (we are interested in P407 for indication of language on the site) 
			local qualifiers = claim.qualifiers or {}
			-- now go through the qualifiers we are interested in (defined in the array qorder above this function)
			for _, qualid in ipairs( qorder ) do
				-- if the claim has this qualifier
				if qualifiers[qualid] then
					-- it's here, let's check it out!
					local items = {}
					-- traverse all snaks in this qualifier
					for _, qualsnak in ipairs( qualifiers[qualid] ) do
						if qualsnak and qual[qualid] then
							-- check if the snak is of the correct snaktype and datatype
							if not (qualsnak.snaktype ~= qual[qualid].types.snaktype
								or qualsnak.datatype ~= qual[qualid].types.datatype)
							then
								-- it is, so let's get the label of the qualifier
								items[1+#items] = {}
								items[#items].text = qual[qualid].formatter(qualsnak.property, qualsnak.datavalue)
								if qualid == 'P407' then
									-- and the language-code
									items[#items].langcode = qual[qualid].langcode(qualsnak.property, qualsnak.datavalue)
								end
								-- if wd hasn't ranked it as preferred, we'll make our own check in the prioritize-list
								preferred = preferred or qual[qualid].preferred(qualsnak.property, qualsnak.datavalue)
							end
						end
					end
					-- special case for P407 (verkets eller navnets språk)
					local text = nil
					if qualid == 'P407' then
						if #items == 0 then
							-- this should never happen, unless it will be possible to add empty qualifiers
							text = mw.message.newRawMessage(g('qualifier-'.. qualid..'-empty', 'qualifier-default-empty')):plain()
						end
						if items[1].langcode then
							local langtext = {}
							for i,v in ipairs(items) do
								if v.text and #v.text>1 then
									langtext[i] = v.text
								end
							end
							langtext = table.concat(langtext, g('initial-P407-combiner'))
							local langcode = {}
							for i,v in ipairs(items) do
								if v.langcode and #v.langcode>1 then
									langcode[i] = v.langcode
								end
							end
							langcode = table.concat(langcode, g('initial-P407-combiner'))
							-- make the URL-link and if the link is ranked as "preferred", it will get a "(foretrukket)"-suffix from the ext-link-preferred format-string 
							--text = mw.message.newRawMessage(g('ext-link-with-lang-' .. claim['rank']), items[1].langcode, items[1].text, mainStr, shortenUri(mainStr)):plain()
							text = mw.message.newRawMessage(g('ext-link-with-lang-' .. claim['rank']), langcode, langtext, mainStr, mainStr):plain()
						else
							-- make the URL-link and if the link is ranked as "preferred", it will get a "(foretrukket)"-suffix from the ext-link-preferred format-string 
							--text = mw.message.newRawMessage(g('ext-link-' .. claim['rank']), mainStr, shortenUri(items[1].text)):plain()
							text = mw.message.newRawMessage(g('ext-link-' .. claim['rank']), mainStr, items[1].text):plain()
						end
						
					else
						-- let's build a list of the qualifiers in the snak
						text = buildList(qualid, items)
					end
					-- add the link to the array
					optionals[1+#optionals] = {}
					optionals[#optionals].text = text
				else
					-- we don't have the qualifier, make do anyway
					local text = nil
					-- special case for P407 (verkets eller navnets språk)
					if qualid == 'P407' then
						-- this will make the label either the URL itself or "no-domene" etc.
						-- text = guess(mainStr)
						-- text = shortenUri(mainStr)
						text = mainStr
					end
					if not text then
						-- nothing available and I don't know how to treat this qualifier.
						-- This shouldn't happen until qorder includes other qualifiers and I don't have any special-case handling beforehand.
						text = mw.message.newRawMessage(
							g('qualifier-'.. pid..'-missing-'..qualid,
								'qualifier-default-missing-'..qualid,
								'qualifier-default-missing')):plain()
					end
					-- make the URL-link and if the link is ranked as "preferred", it will get a "(foretrukket)"-suffix from the ext-link-preferred format-string
					optionals[1+#optionals] = {}
					optionals[#optionals].text = mw.message.newRawMessage(g('ext-link-' .. claim['rank']), mainStr, text):plain()
				end
			end
			if #optionals > 0 then
				-- build the list from the claims so far
				mainStr = buildList(pid, optionals)
			end
			if preferred then
				-- if these claims are "preferred" or in the prioritize list, let's put them in the "head"-queue
				head[1+#head] = {}
				head[#head].text = mainStr
			else
				-- else, let's put them in the "tail"-queue
				tail[1+#tail] = {}
				tail[#tail].text = mainStr
			end
		end
	end
	return head, tail
end

local function addLinkback(str, property)
	local id = mw.wikibase.getEntityObject()
	if not id then
		return str
	end
	if type(id) == 'table' then
		id = id.id
	end
	
	local class = ''
	if property then
		class = 'wd_' .. string.lower(property)
	end
	local title = g('wikidata-linkback-edit')
	local icon = '[%s [[File:Blue pencil.svg|%s|10px|baseline|link=]] ]'
	local url = mw.uri.fullUrl('d:' .. id .. '#' .. property)
	url.fragment = property
	url = tostring(url)
	local v = mw.html.create('span')
		:addClass(class)
		:wikitext(str)
		:tag('span')
			:addClass('noprint plainlinks wikidata-linkback')
			:css('padding-left', '.5em')
			:wikitext(icon:format(url, title))
		:allDone()
	return tostring(v)
end

function p.links( frame )
	local items = {}
	local counts = {}
	-- find all arguments to this function 
	for _,v in ipairs(frame.args) do
		-- we expect arguments of the following kind: P856, Pxxx and so on (where xxx is a number)
		local _, _, ch, num= v:find("([pP])(%d+)")
		-- If it start with a "P", we'll assume it's a valid property
		if ch then
			-- let's get the property ID (Pxxx)
			local pid = ch:upper()..num
			-- now find the label for this property.
			local label = findEntityLabel( pid )
			if not counts[pid] then
				counts[pid] = 0
			end
			if label then
				-- now find the available links for this property, separated into two groups, a "head"-group which are the preferred links and a "tail"-group which are normal links
				local head, tail = p.findMainLinks(pid)
				if (head and #head > 0) or (tail and #tail > 0) then
					-- we got something, so let's count it!
					counts[pid] = counts[pid] + 1
				end
				if head and #head > 0 then
					-- we have a "head"-group, so let's add it and the tail-group as well
					-- items[1+#items] = { text = { contLang:ucfirst( label ), buildList('', head), wrap(buildList('', tail), pid) }, pid=pid }
					for i,v in ipairs(head) do
						items[1+#items] = { text = { contLang:ucfirst( label ), v.text }, pid=pid }
					end
					if tail and #tail > 0 then
						items[1+#items] = { text = { contLang:ucfirst( label ), wrap(buildList('', tail), pid) }, pid=pid }
					end
				elseif tail and #tail > 0 then
					-- we only got a "tail"-group, so no head for you..
					-- items[1+#items] = { text = { contLang:ucfirst( label ), buildList('', tail) }, pid=pid }
					for i,v in ipairs(tail) do
						items[1+#items] = { text = { contLang:ucfirst( label ), v.text }, pid=pid }
					end
				end
			end
		end
	end
	for i,v in ipairs(items) do
		-- if we have three parts (label, head and tail), we'll use the format-string with 3 variables ($1, $2 and $3), which will map into the items as made before
		-- if we have two parts (label and head or tail), we'll use the format-string with 2 variables ($1 and $2), which will map into the items as made before
		-- this function will use the format-string and the items as input to output the correct strings.
		--items[i] = (i==1 and '' or '* ') .. addLinkback(mw.message.newRawMessage(
		--	((#v.text >=3 and #v.text[3])
		--		and g('first-list-item-with-additional')
		--		or g('first-list-item')),
		--	unpack(v.text))
		--:plain(), v.pid)
		items[i] = (i==1 and '' or '* ') .. addLinkback(mw.message.newRawMessage(
			g('first-list-item'),
			unpack(v.text))
		:plain(), v.pid)
	end
	local cats = {}
	for k,v in pairs(counts) do
		cats[1+#cats] = '[[Category:' .. mw.message.newRawMessage(g(counts[k]==0 and 'cat-exclusion' or 'cat-inclusion'), findEntityLabel( k )):plain() .. ']]'
	end
	if #items > 0 then
		return table.concat(items, "\n") .. table.concat(cats, "")
	end
	return "''" .. mw.message.newRawMessage(g('no-links-available')):plain() .. "''" .. table.concat(cats, "")
end

return p